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

std: Switch from libbacktrace to gimli (take 2) #74682

Merged
merged 1 commit into from
Jul 31, 2020

Conversation

alexcrichton
Copy link
Member

This is the second attempt to land #73441 after being reverted in #74613. Will be gathering precise perf numbers here in this take.

Closes #71060

@rust-highfive
Copy link
Collaborator

r? @Mark-Simulacrum

(rust_highfive has picked a reviewer for you, use r? to override)

@rust-highfive
Copy link
Collaborator

⚠️ Warning ⚠️

  • These commits modify submodules.

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Jul 23, 2020
@Mark-Simulacrum
Copy link
Member

@bors try @rust-timer queue

@rust-timer
Copy link
Collaborator

Awaiting bors try build completion

@bors
Copy link
Contributor

bors commented Jul 23, 2020

⌛ Trying commit b2af228df5797b8cb106ca82b5a940f136cc40e4 with merge ca43ac58855c4f873de3e71b58ae3823e4290de9...

@bors
Copy link
Contributor

bors commented Jul 23, 2020

☀️ Try build successful - checks-actions, checks-azure
Build commit: ca43ac58855c4f873de3e71b58ae3823e4290de9 (ca43ac58855c4f873de3e71b58ae3823e4290de9)

@rust-timer
Copy link
Collaborator

Queued ca43ac58855c4f873de3e71b58ae3823e4290de9 with parent 371917a, future comparison URL.

@rust-timer
Copy link
Collaborator

Finished benchmarking try commit (ca43ac58855c4f873de3e71b58ae3823e4290de9): comparison url.

Benchmarking this pull request likely means that it is perf-sensitive, so we're automatically marking it as not fit for rolling up. Please note that if the perf results are neutral, you should likely undo the rollup=never given below by specifying rollup- to bors.

Importantly, though, if the results of this run are non-neutral do not roll this PR up -- it will mask other regressions or improvements in the roll up.

@bors rollup=never

@mati865
Copy link
Contributor

mati865 commented Jul 24, 2020

run_linker is still significant, the other big slowdowns are as expected: metadata_register_crate and metadata_decode_entry.
I should be able to look into it next week.

@Mark-Simulacrum
Copy link
Member

cc #74716 which is another revert of basically a major expansion of code in libstd.

I expect that this basically means that the current implementation of the metadata loading and such in the compiler just deals really poorly with expanding dependencies (at least std). This seems to match up with what @alexcrichton has said.

Hopefully, these two cases can both be resolved with similar modifications to the metadata loading in the compiler.

@alexcrichton
Copy link
Member Author

@Mark-Simulacrum @nnethercote do y'all know what the next steps are here? This has basically been silent since the perf results came back?

@Mark-Simulacrum
Copy link
Member

Yeah, I've been bogged down with the perf server being overloaded due to the increased speed of rust-lang/rust CI (due to GHA). I'm hoping to have some cycles this week to take a look at the performance here, probably by pushing some commits to this branch and/or doing some local benchmarking.

I also noticed that @petrochenkov noted #74807 as likely an improvement for this PR.

I can take on rebasing this and such as needed; my guess is that we'll probably take this week to examine if there's anything we can do here and then prepare a report for next week's compiler team meeting where the benefits/costs of this PR will be evaluated. I believe that we're likely to decide that the soundness and cross-compiling nature of std is more important than the performance losses here -- which are all mostly on smaller crates, and measure in large percentages but are absolutely small -- and as such approve this PR.

@alexcrichton
Copy link
Member Author

I'm happy to take on the rebasing, I just want to make sure this isn't lost in the shuffle. That was the purpose of not reverting it in the first place, it's easily forgotten. Is there anything that needs to be done to place this on an agenda?

@mati865
Copy link
Contributor

mati865 commented Jul 28, 2020

I think LTOing backtrace crate would reduce linker regression at the expense of Rust's build time but haven't really got time to test that yet.

@mark-i-m
Copy link
Member

Just a heads up that backtrace should be added to library/ now instead of src/.

@alexcrichton alexcrichton force-pushed the backtrace-gimli-round-2 branch from b2af228 to f7bc182 Compare July 28, 2020 16:25
@nnethercote
Copy link
Contributor

@alexcrichton: I'm happy to do some profiling, but I can't manage to apply the patch so that it builds locally, even after @Mark-Simulacrum helped me. Is the contents of .gitmodules correct? It mentions src/backtrace. Can you give me a single command to download and apply the patch? Thanks.

@alexcrichton
Copy link
Member Author

AFAIK this can't be downloaded as a patch because it adds a submodule, you'll need to checkout the git commit or do a cherry-pick. I'll fix the .gitmodules issue but I don't think that will help with apply this PR as a patch.

This commit is a proof-of-concept for switching the standard library's
backtrace symbolication mechanism on most platforms from libbacktrace to
gimli. The standard library's support for `RUST_BACKTRACE=1` requires
in-process parsing of object files and DWARF debug information to
interpret it and print the filename/line number of stack frames as part
of a backtrace.

Historically this support in the standard library has come from a
library called "libbacktrace". The libbacktrace library seems to have
been extracted from gcc at some point and is written in C. We've had a
lot of issues with libbacktrace over time, unfortunately, though. The
library does not appear to be actively maintained since we've had
patches sit for months-to-years without comments. We have discovered a
good number of soundness issues with the library itself, both when
parsing valid DWARF as well as invalid DWARF. This is enough of an issue
that the libs team has previously decided that we cannot feed untrusted
inputs to libbacktrace. This also doesn't take into account the
portability of libbacktrace which has been difficult to manage and
maintain over time. While possible there are lots of exceptions and it's
the main C dependency of the standard library right now.

For years it's been the desire to switch over to a Rust-based solution
for symbolicating backtraces. It's been assumed that we'll be using the
Gimli family of crates for this purpose, which are targeted at safely
and efficiently parsing DWARF debug information. I've been working
recently to shore up the Gimli support in the `backtrace` crate. As of a
few weeks ago the `backtrace` crate, by default, uses Gimli when loaded
from crates.io. This transition has gone well enough that I figured it
was time to start talking seriously about this change to the standard
library.

This commit is a preview of what's probably the best way to integrate
the `backtrace` crate into the standard library with the Gimli feature
turned on. While today it's used as a crates.io dependency, this commit
switches the `backtrace` crate to a submodule of this repository which
will need to be updated manually. This is not done lightly, but is
thought to be the best solution. The primary reason for this is that the
`backtrace` crate needs to do some pretty nontrivial filesystem
interactions to locate debug information. Working without `std::fs` is
not an option, and while it might be possible to do some sort of
trait-based solution when prototyped it was found to be too unergonomic.
Using a submodule allows the `backtrace` crate to build as a submodule
of the `std` crate itself, enabling it to use `std::fs` and such.

Otherwise this adds new dependencies to the standard library. This step
requires extra attention because this means that these crates are now
going to be included with all Rust programs by default. It's important
to note, however, that we're already shipping libbacktrace with all Rust
programs by default and it has a bunch of C code implementing all of
this internally anyway, so we're basically already switching
already-shipping functionality to Rust from C.

* `object` - this crate is used to parse object file headers and
  contents. Very low-level support is used from this crate and almost
  all of it is disabled. Largely we're just using struct definitions as
  well as convenience methods internally to read bytes and such.

* `addr2line` - this is the main meat of the implementation for
  symbolication. This crate depends on `gimli` for DWARF parsing and
  then provides interfaces needed by the `backtrace` crate to turn an
  address into a filename / line number. This crate is actually pretty
  small (fits in a single file almost!) and mirrors most of what
  `dwarf.c` does for libbacktrace.

* `miniz_oxide` - the libbacktrace crate transparently handles
  compressed debug information which is compressed with zlib. This crate
  is used to decompress compressed debug sections.

* `gimli` - not actually used directly, but a dependency of `addr2line`.

* `adler32`- not used directly either, but a dependency of
  `miniz_oxide`.

The goal of this change is to improve the safety of backtrace
symbolication in the standard library, especially in the face of
possibly malformed DWARF debug information. Even to this day we're still
seeing segfaults in libbacktrace which could possibly become security
vulnerabilities. This change should almost entirely eliminate this
possibility whilc also paving the way forward to adding more features
like split debug information.

Some references for those interested are:

* Original addition of libbacktrace - rust-lang#12602
* OOM with libbacktrace - rust-lang#24231
* Backtrace failure due to use of uninitialized value - rust-lang#28447
* Possibility to feed untrusted data to libbacktrace - rust-lang#21889
* Soundness fix for libbacktrace - rust-lang#33729
* Crash in libbacktrace - rust-lang#39468
* Support for macOS, never merged - ianlancetaylor/libbacktrace#2
* Performance issues with libbacktrace - rust-lang#29293, rust-lang#37477
* Update procedure is quite complicated due to how many patches we
  need to carry - rust-lang#50955
* Libbacktrace doesn't work on MinGW with dynamic libs - rust-lang#71060
* Segfault in libbacktrace on macOS - rust-lang#71397

Switching to Rust will not make us immune to all of these issues. The
crashes are expected to go away, but correctness and performance may
still have bugs arise. The gimli and `backtrace` crates, however, are
actively maintained unlike libbacktrace, so this should enable us to at
least efficiently apply fixes as situations come up.
@alexcrichton alexcrichton force-pushed the backtrace-gimli-round-2 branch from f7bc182 to 06d565c Compare July 28, 2020 23:34
@nnethercote
Copy link
Contributor

Here is a Cachegrind diff for helloworld, which should be representative.

--------------------------------------------------------------------------------
Ir                   file:function
--------------------------------------------------------------------------------
 1,356,720 ( 6.95%)  /home/njn/moz/rustN/library/core/src/str/mod.rs:core::str::from_utf8
   759,853 ( 3.89%)  /home/njn/moz/rustN/src/librustc_serialize/leb128.rs:rustc_serialize::serialize::Decoder::read_option
   749,805 ( 3.84%)  /home/njn/moz/rustN/src/librustc_hir/definitions.rs:<rustc_hir::definitions::DefKey as rustc_serialize::serialize::Decodable>::decode
   576,010 ( 2.95%)  /home/njn/.cargo/registry/src/gh.neting.cc-1ecc6299db9ec823/rustc-hash-1.1.0/src/lib.rs:_ZN59_$LT$rustc_hash..FxHasher$u20$as$u20$core..hash..Hasher$GT$5write17hb6bb59a12824277fE.llvm.NNN 
   509,976 ( 2.61%)  /home/njn/moz/rustN/src/librustc_serialize/leb128.rs:<rustc_hir::definitions::DefKey as rustc_serialize::serialize::Decodable>::decode
   475,531 ( 2.44%)  /home/njn/moz/rustN/src/librustc_serialize/opaque.rs:<rustc_span::SourceFile as rustc_serialize::serialize::Decodable>::decode
   456,418 ( 2.34%)  /home/njn/.cargo/registry/src/gh.neting.cc-1ecc6299db9ec823/hashbrown-0.6.2/src/raw/mod.rs:hashbrown::raw::RawTable<T>::insert
   416,754 ( 2.13%)  /home/njn/moz/rustN/src/librustc_metadata/rmeta/decoder.rs:<rustc_metadata::rmeta::decoder::DecodeContext as rustc_serialize::serialize::SpecializedDecoder<rustc_span::span_encoding::Span>>::specialized_decode
   399,952 ( 2.05%)  ???:llvm::StringMapImpl::LookupBucketFor(llvm::StringRef)
   375,778 ( 1.92%)  /home/njn/moz/rustN/src/librustc_serialize/opaque.rs:_ZN88_$LT$rustc_serialize..opaque..Decoder$u20$as$u20$rustc_serialize..serialize..Decoder$GT$8read_str17h01d5500dbce54e65E.llvm.NNN
   366,560 ( 1.88%)  /home/njn/moz/rustN/src/librustc_serialize/serialize.rs:rustc_serialize::serialize::Decoder::read_option
   329,920 ( 1.69%)  /build/glibc-YYA7BZ/glibc-2.31/malloc/malloc.c:_int_malloc
   321,586 ( 1.65%)  /home/njn/.cargo/registry/src/gh.neting.cc-1ecc6299db9ec823/hashbrown-0.6.2/src/raw/mod.rs:scoped_tls::ScopedKey<T>::with
   321,192 ( 1.64%)  /home/njn/.cargo/registry/src/gh.neting.cc-1ecc6299db9ec823/scoped-tls-1.0.0/src/lib.rs:scoped_tls::ScopedKey<T>::with
   304,226 ( 1.56%)  /home/njn/.cargo/registry/src/gh.neting.cc-1ecc6299db9ec823/hashbrown-0.6.2/src/map.rs:hashbrown::map::HashMap<K,V,S>::insert
   298,526 ( 1.53%)  /home/njn/moz/rustN/library/alloc/src/vec.rs:<rustc_span::SourceFile as rustc_serialize::serialize::Decodable>::decode
   295,320 ( 1.51%)  /build/glibc-YYA7BZ/glibc-2.31/elf/../sysdeps/x86_64/tls_get_addr.S:__tls_get_addr
   267,966 ( 1.37%)  /build/glibc-YYA7BZ/glibc-2.31/malloc/malloc.c:_int_free
   213,647 ( 1.09%)  /build/glibc-YYA7BZ/glibc-2.31/string/../sysdeps/x86_64/multiarch/memcmp-avx2-movbe.S:__memcmp_avx2_movbe
   205,266 ( 1.05%)  /home/njn/moz/rustN/src/librustc_serialize/leb128.rs:<rustc_metadata::rmeta::decoder::DecodeContext as rustc_serialize::serialize::SpecializedDecoder<rustc_span::span_encoding::Span>>::specialized_decode
   192,838 ( 0.99%)  /build/glibc-YYA7BZ/glibc-2.31/malloc/malloc.c:malloc
   183,278 ( 0.94%)  /home/njn/moz/rustN/library/core/src/slice/mod.rs:<rustc_metadata::rmeta::decoder::DecodeContext as rustc_serialize::serialize::SpecializedDecoder<rustc_span::span_encoding::Span>>::specialized_decode
   179,183 ( 0.92%)  /home/njn/.cargo/registry/src/gh.neting.cc-1ecc6299db9ec823/hashbrown-0.6.2/src/raw/mod.rs:hashbrown::map::HashMap<K,V,S>::insert
   174,984 ( 0.90%)  /home/njn/moz/rustN/src/librustc_serialize/serialize.rs:rustc_serialize::serialize::Decoder::read_seq
   173,436 ( 0.89%)  /home/njn/moz/rustN/src/librustc_serialize/leb128.rs:_ZN88_$LT$rustc_serialize..opaque..Decoder$u20$as$u20$rustc_serialize..serialize..Decoder$GT$8read_str17h01d5500dbce54e65E.llvm.NNN
   173,341 ( 0.89%)  /home/njn/moz/rustN/library/alloc/src/vec.rs:rustc_serialize::serialize::Decoder::read_seq
   158,871 ( 0.81%)  /home/njn/moz/rustN/src/librustc_query_system/query/plumbing.rs:rustc_query_system::query::plumbing::get_query_impl
   154,258 ( 0.79%)  ???:llvm::PassRegistry::registerPass(llvm::PassInfo const&, bool)
   149,634 ( 0.77%)  /home/njn/moz/rustN/library/core/src/num/mod.rs:core::str::from_utf8
   146,524 ( 0.75%)  /home/njn/moz/rustN/src/librustc_serialize/opaque.rs:<rustc_hir::definitions::DefKey as rustc_serialize::serialize::Decodable>::decode
   144,218 ( 0.74%)  /build/glibc-YYA7BZ/glibc-2.31/string/../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S:__memcpy_avx_unaligned_erms
   143,430 ( 0.73%)  /home/njn/moz/rustN/src/librustc_span/symbol.rs:<rustc_hir::definitions::DefKey as rustc_serialize::serialize::Decodable>::decode

Looks like it's all due to more metadata encoding.

@alexcrichton
Copy link
Member Author

Is there something I need to do to ensure that this comes up on the appropriate agenda? If so, what is that?

@Mark-Simulacrum
Copy link
Member

I don't think so -- I plan to spend some more time this week looking at that cachegrind diff and performance here in general (and have already posted at least one PR, #74887 which should have a ~1% improvement on the benchmarks that regressed here). I think once that's done we'll want to re-run try here for some "final numbers" and nominate this PR for the compiler team.

My expectation is that it will then be discussed on August 6th and likely landed soon thereafter.

@alexcrichton
Copy link
Member Author

Is it possible to bump this sooner on the agenda? It was intended to land this just after the beta branch to give this maximal testing on nightly to weed out unexpected regressions. Landing after August 6th delays that by 3 weeks and halves the amount of nightly testing this is going to get.

I'll reiterate I'm more than happy to revert this if the decision is that it shouldn't land. Given though that this is highly likely to land I continue to not understand the very strong reluctance to put this in tree.

@alexcrichton
Copy link
Member Author

Is there a way to test that ahead of time? I'm happy to do any testing for that, I just figured it wasn't really testable until this merged

@Mark-Simulacrum
Copy link
Member

Hm okay so rust-src is broken right now anyway as a result of the src -> library move... but that should have been fixed by #74923. I guess the try build here kicked off a few hours before that merged, though.

@bors try

I (tried to) test the current try build by doing this:

cargo new --lib foo && cd foo
$ rustup-toolchain-install-master -c rust-src -- 59c859c74c32bb26ba7c0932ec50c6
c566eeac76
detecting the channel of the `59c859c74c32bb26ba7c0932ec50c6c566eeac76` toolchain...
downloading <https://ci-artifacts.rust-lang.org/rustc-builds/59c859c74c32bb26ba7c0932ec50c6c566eeac76/rustc-nightly-x86_64-unknown-linux-gnu.tar.xz>...
50.63 MB / 50.63 MB [=====================================================] 100.00 % 13.00 MB/s
downloading <https://ci-artifacts.rust-lang.org/rustc-builds/59c859c74c32bb26ba7c0932ec50c6c566eeac76/rust-src-nightly.tar.xz>...
1.76 MB / 1.76 MB [========================================================] 100.00 % 4.44 MB/s
downloading <https://ci-artifacts.rust-lang.org/rustc-builds/59c859c74c32bb26ba7c0932ec50c6c566eeac76/rust-std-nightly-x86_64-unknown-linux-gnu.tar.xz>...
20.73 MB / 20.73 MB [=====================================================] 100.00 % 12.14 MB/s
toolchain `59c859c74c32bb26ba7c0932ec50c6c566eeac76` is successfully installed!
$ cargo +59c859c74c32bb26ba7c0932ec50c6c566eeac76 -Zbuild-std build --target x86_64-unknown-linux-gnu
error: failed to read `/home/mark/.rustup/toolchains/59c859c74c32bb26ba7c0932ec50c6c566eeac76/lib/rustlib/src/rust/src/libstd/Cargo.toml`

Caused by:
  No such file or directory (os error 2)

(Obviously, we expect that to succeed).

@bors
Copy link
Contributor

bors commented Jul 30, 2020

⌛ Trying commit 06d565c with merge 545470a83a069207bfd714fe0d7010132e3d36fc...

@bors
Copy link
Contributor

bors commented Jul 30, 2020

☀️ Try build successful - checks-actions, checks-azure
Build commit: 545470a83a069207bfd714fe0d7010132e3d36fc (545470a83a069207bfd714fe0d7010132e3d36fc)

@alexcrichton
Copy link
Member Author

Needed to add -c cargo there too to get Cargo installed, but after that I can confirm that this does indeed work with -Zbuild-std

@Mark-Simulacrum
Copy link
Member

@bors r+ rollup=never in that case. Thanks for bearing with us on this.

@bors
Copy link
Contributor

bors commented Jul 30, 2020

📌 Commit 06d565c has been approved by Mark-Simulacrum

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jul 30, 2020
@bors
Copy link
Contributor

bors commented Jul 30, 2020

⌛ Testing commit 06d565c with merge 66b9e8cfe2bb84b38eae2fb4eb6003ee1663791c...

@Manishearth
Copy link
Member

@bors retry yield

@rust-log-analyzer
Copy link
Collaborator

Your PR failed (pretty log, raw log). Through arcane magic we have determined that the following fragments from the build log may contain information about the problem.

Click to expand the log.
##[group]Run exit 1
exit 1
shell: /bin/bash --noprofile --norc -e -o pipefail {0}
##[endgroup]
##[error]Process completed with exit code 1.

I'm a bot! I can only do what humans tell me to, so if this was not helpful or you have suggestions for improvements, please ping or otherwise contact @rust-lang/infra. (Feature Requests)

@Manishearth
Copy link
Member

@bors treeclosed-

@bors
Copy link
Contributor

bors commented Jul 30, 2020

⌛ Testing commit 06d565c with merge c058a8b...

@bors
Copy link
Contributor

bors commented Jul 31, 2020

☀️ Test successful - checks-actions, checks-azure
Approved by: Mark-Simulacrum
Pushing c058a8b to master...

@bors bors added the merged-by-bors This PR was explicitly merged by bors. label Jul 31, 2020
@bors bors merged commit c058a8b into rust-lang:master Jul 31, 2020
@Mark-Simulacrum
Copy link
Member

As expected, this was a performance regression of up to 20% on smaller crates, essentially entirely due to increased amount of metadata decoding. #75008 may be the first step in eliminating these losses.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
merged-by-bors This PR was explicitly merged by bors. S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Backtraces don't work during ICE