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

Add basic backtrace functionality #12070

Closed
wants to merge 2 commits into from

Conversation

alexcrichton
Copy link
Member

Whenever a failure happens, if a program is run with
RUST_LOG=std::rt::backtrace a backtrace will be printed to the task's stderr
handle. Stack traces are uncondtionally printed on double-failure and
rtabort!().

Closes #10128


This PR is an improvement over the previous iteration in a few ways:

  • This now works on all platforms
  • This works on green threads as well as native threads

I have yet to actually get symbols from backtraces on windows, but at this point I'm convinced that it's something wrong my my VM installation. All documentation and examples I can find point to doing almost exactly what's impemented. The good news is that the only part in question for the windows implementation is the address => symbol translation (stack walking is working just fine).

/// print everything as a backtrace.
///
/// Sadly we don't have filenames/line numbers at this point, but hey, at least
/// this is better than nothing!.
Copy link
Member

Choose a reason for hiding this comment

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

typo: extra .

@alexcrichton
Copy link
Member Author

Updated with comments (plus a much better test)

@wycats
Copy link
Contributor

wycats commented Feb 7, 2014

👍 This is a huge win

let out = p.finish_with_output();
assert!(!out.status.success());
let s = str::from_utf8(out.error).unwrap();
assert!(s.contains("stack backtrace") && s.contains("foo::h"));
Copy link
Member

Choose a reason for hiding this comment

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

What's the ::h?

Copy link
Member Author

Choose a reason for hiding this comment

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

This is just making sure that we're demangling at least a little bit. Each symbol is of the form path::h<hash>, so we should be guaranteed that with the foo function in the backtrace that foo::h should show up somewhere.

@alexcrichton
Copy link
Member Author

Pushed with comments addressed

@emberian
Copy link
Member

Can we enable backtracing by default? Maybe just for uncaught failure?

@alexcrichton
Copy link
Member Author

Perhaps, I'm afraid to do this too often, I figured task failure could be semi-normal in some circumstances, so I figured it would be better to opt in instead of opt out.

Perhaps we should get some mileage with this added first though. I don't want to overwhelm tests too much with backtraces everywhere if that turns out to be the normal case.

@alexcrichton
Copy link
Member Author

Hm, it appears that this doesn't seem to be that useful on linux at all. From what I can tell, dladdr only consults the dynamic symbol table, whereas most of our symbols are considered being in the local symbol table. I am unable to find anything which consults the local symbol table instead of the dynamic symbol table, and otherwise fixing this would require us not flagging functions as internal to LLVM and then passing the --export-dynamic option to GCC (or maybe -rdynamic), which seems like overkill for backtraces.

With this in light, here's a summary of the backtrace methods that I know of:

backtrace

This didn't work well on OSX because it couldn't backtrace green threads. This also doesn't work well on linux because it can't print the names of all our internal functions.

libgcc_s

We depend on this library for unwinding, and it conveniently provides an api to trace the stack. This method proved much more reliable for backtrace, working in all situations. Sadly, all it gives us is IP addresses, nothing else. It's up to us to translate the address to a name, and the method of dladdr has the limitations laid out above.

libunwind

Turns out using libunwind directly there's a unw_get_proc_name function which does exactly what we want. In my testing, it figured out the names of static C functions, and this translates to working for all our internal functions as well.

The problem with using libunwind is that I don't think we can rely on this being available on all systems. I had to install a package to get it on ubuntu, and even then I couldn't get the 32-bit version of libunwind. We could build it ourselves, but then we're having a new dependency for all programs (just for backtraces), so I'm not sure this solution will work out.

readelf

We know that libgcc_s + dladdr works except that it can't resolve all of our symbols. The return value from dladdr does give us a filename that the symbol comes from, plus we have the exact symbol of the address (thanks to _Unwind_FindEnclosingFunction). In theory we could just shell out to readelf (as suggested by this stackoverflow thread) and parse that output.

This relies on readelf being installed on the system, but that's pretty common for non-osx unixes I believe. This method is also super sketchy.

Moving forward

I don't think that "just using dladdr" will cut it for non-osx unices. I actually think that the nicest way forward will be shelling out to readelf. This symbol problem is currently only an issue on platforms that predominately use ELF-format executables/objects, so I don't think there's too much of a problem there.

What do others think about this approach?

@alexcrichton
Copy link
Member Author

As another option, we could also modify codegen to emit a symbol table into the crate map and we could iterate the crate maps looking for the right symbol. I'm not sure if this can be done super efficiently, but it would in theory be robust enough to get us away from readelf.

@vadimcn
Copy link
Contributor

vadimcn commented Feb 17, 2014

Wouldn't it be simpler to build with line number debug info? (the equivalent of the old -Z debug-info option, if that still exists in some form).

@alexcrichton
Copy link
Member Author

How would debug info help this? The backtrace functionality doesn't need debug info, the only functionality we need is translation from an address to a symbol name. I'm unaware of a lightweight library which does that. Unless we're willing to bundle our own dwarf parser, I'm not sure that debug info would help us much.

@vadimcn
Copy link
Contributor

vadimcn commented Feb 17, 2014

dwarf debug info includes address <-> symbol mapping and it would be the most universal way to get symbol names across all platforms. I think libunwind did so well in your tests because it does parse dwarf debug info.
AFAIK, libgcc's backtrace() uses ELF .symtab (which I would also consider debug info, btw), but that won't be available on platforms which don't use ELF. And, apparently, it's only emitted for shared libraries (but maybe there's some linker option that overrides this?).

@alexcrichton
Copy link
Member Author

Hm, do you know how to get access to this data? I am unaware of any debug info that we emit other than that necessary to unwind a frame on the stack. Does the unwinding api from libgcc_s expose symbol functionality at all? That would be the best way to go about this.

@vadimcn
Copy link
Contributor

vadimcn commented Feb 17, 2014

gcc unwinding code does not do symbol lookup, but it has libbacktrace bundled with it does. But I thought you've already tried libbacktrace (or else what does 'backtrace' above refer to?)

@alexcrichton
Copy link
Member Author

Ah, by backtrace I meant the backtrace function. Is libbacktrace included in libgcc_s by default? If so we could probably use that for now instead of the unwind apis.

@vadimcn
Copy link
Contributor

vadimcn commented Feb 18, 2014

I think it's a static lib (I'm talking about his one).
Also, looks like mingw doesn't bundle it (probably because Windows doesn't use ELF binaries), so no, unfortunately you can't rely on it by default :(.

@pnkfelix
Copy link
Member

cc me

Whenever a failure happens, if a program is run with
`RUST_LOG=std::rt::backtrace` a backtrace will be printed to the task's stderr
handle. Stack traces are uncondtionally printed on double-failure and
rtabort!().

Closes rust-lang#10128
@alexcrichton
Copy link
Member Author

I'm in the process of integrating libbacktrace for linux. I'm going to close this and open a subsequent PR to get a clean slate for discussion.

bors added a commit to rust-lang-ci/rust that referenced this pull request Jul 25, 2022
minor: clarify error message

Clarify that the server is a whole is OK, and that it's only a single
requests that's dead
flip1995 pushed a commit to flip1995/rust that referenced this pull request Feb 26, 2024
Fix issue rust-lang#12034: add autofixes for unnecessary_fallible_conversions

fixes rust-lang#12034

Currently, the `unnecessary_fallible_conversions` lint was capable of autofixing expressions like `0i32.try_into().unwrap()`. However, it couldn't autofix expressions in the form of `i64::try_from(0i32).unwrap()` or `<i64 as TryFrom<i32>>::try_from(0).unwrap()`.

This pull request extends the functionality to correctly autofix these latter forms as well.

changelog: [`unnecessary_fallible_conversions`]: Add autofixes for more forms
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add basic backtrace functionality
7 participants