-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
Symbols blow up binary size to an unreasonable degree on x86_64-unknown-linux-gnu #46034
Comments
On x86_64-apple-darwin:
The size is definitely growing but not something that increases by 10×. On x86_64-pc-windows-msvc:
Probably a Linux-only problem. |
@kennytm updated OP with |
Repro'd on Linux with stable. (Not sure about the BSDs)
The initial blow up problem comes from 1.14.0. Comparing the readelf -S -W 1.13.0
readelf -S -W 1.14.0
Detailed breakdown of size delta for each section
|
#43392 seems to be the same problem. Since Windows and macOS places the debug symbols externally (*.pdb and *.dSYM respectively), we don't see the size blow up on those platforms. |
This seems like a very important thing. Size affects linkage time, affects compilation speed. |
Making the assumption that this is caused by |
On
So stripping debug info goes from 12M to 6.3M. |
See rust-lang/rust#46034 [skip CI]
The C/C++ toolchain (whether GCC or Clang) on Linux solves this with -gsplit-dwarf, which results in similar products as the *.pdb or *.dSYM on Windows and macOS; to anyone interested in implementing the support, it would be as simple as setting the appropriate LLVM codegen option (as seen in clang):
|
Oh interesting. cc @main-- rust-lang/rfcs#2154. |
Thanks. I actually knew about this "debug fission" but was not aware that LLVM already supports it. The nice thing about the old How good is toolchain support? Of course gdb reads info from dwo files but GNU addr2line does not. It's either just not implemented yet or not intended to work like that (which would be very disappointing since you need I'm still having a hard time finding pretty much any documentation at all about debug fissure (beyond that one GCC page) but these points lead me to believe that it's not the best approach for what I'm trying to do in rust-lang/rfcs#2154. |
But... if |
Yes. |
@michaelwoerister this is kind of true, but only kind of... Measurements in https://users.rust-lang.org/t/rust-binary-sizes-once-again/16287/2 for a crate with "with rocket, serde, wiringpi and pam-auth" should reduction 6.5M -> 1.6M. It is not 90%, but, still, is very significant. Note also an interesting observation that LTO does not affect stripped binaries, but, for non-striped, goes from 6.5M -> 4.7M. I wonder if there's any actionable items on this issue? Could there be some linker flag for "please, don't link-in the debuginfo into the final binary"? |
That's a good idea, @matklad. |
If anyone wants to give it a try, the relevant spot in the source code is here: rust/src/librustc_trans/back/linker.rs Lines 283 to 285 in 15add36
This method should add any linker flags relevant to debuginfo. |
I also stumbled upon this cross-compiling from OS X to arm-linux: https://users.rust-lang.org/t/rust-binary-sizes-once-again/16287/2 |
…ister Pass --strip-debug to GccLinker when building without debuginfo C.f. #46034 --- This brings a hello-world built by passing rustc no command line options from 2.9M to 592K on Linux. (This might need to special case MacOS or Windows, not sure if the linkers there support `--strip-debug`, and there is an annoying lack of dependable docs for the linkers there.)
just tested with hello world. running (Running rustc 1.38) |
@elichai As explained in the PR you need to pass |
Our Linux release binary was hilariously large, weighing in at nearly 800MB (!). Nearly all of the bloat was from DWARF debug info: $ bloaty materialized -n 10 FILE SIZE VM SIZE -------------- -------------- 24.5% 194Mi 0.0% 0 .debug_info 24.1% 191Mi 0.0% 0 .debug_loc 13.8% 109Mi 0.0% 0 .debug_pubtypes 10.1% 79.9Mi 0.0% 0 .debug_pubnames 8.8% 70.0Mi 0.0% 0 .debug_str 8.3% 66.3Mi 0.0% 0 .debug_ranges 4.4% 35.3Mi 0.0% 0 .debug_line 3.1% 24.8Mi 66.3% 24.8Mi .text 1.8% 14.4Mi 25.1% 9.39Mi [41 Others] 0.6% 4.79Mi 0.0% 0 .strtab 0.4% 3.22Mi 8.6% 3.22Mi .eh_frame 100.0% 793Mi 100.0% 37.4Mi TOTAL This patch gets a handle on this by attacking the problem from several angles: 1. We instruct the linker to compress debug info sections. Most of the debug info is redundant and compresses exceptionally well. Part of the reason we didn't notice the issue is because our Docker images and gzipped tarballs were relatively small (~150MB). 2. We strip out the unnecessary `.debug_pubnames` and `.debug_pubtypes` from the binary. This works around a known Rust bug (rust-lang/rust#46034). 3. We ask Rust to generate less debug info for release builds, limiting it to line info. This is enough information to symbolicate a backtrace, but not enough information to run an interactive debugger. This is usually the right tradeoff for a release build. $ bloaty materialized -n 10 VM SIZE FILE SIZE -------------- -------------- 0.0% 0 .debug_info 31.9Mi 33.8% 70.5% 25.0Mi .text 25.0Mi 26.5% 0.0% 0 .debug_str 7.54Mi 8.0% 0.0% 0 .debug_line 6.36Mi 6.7% 9.4% 3.33Mi [38 Others] 5.36Mi 5.7% 0.0% 0 .strtab 4.71Mi 5.0% 0.0% 0 .debug_ranges 3.55Mi 3.8% 8.8% 3.11Mi .eh_frame 3.11Mi 3.3% 0.0% 0 .symtab 2.87Mi 3.0% 6.0% 2.12Mi .rodata 2.12Mi 2.2% 5.4% 1.92Mi .gcc_except_table 1.92Mi 2.0% 100.0% 35.5Mi TOTAL 94.4Mi 100.0% One issue remains unsolved, which is that Rust/LLVM cannot currently garbage collect DWARF that refers to unused symbols/types. The actual symbols get cut from the binary, but their debug info remains. Follow rust-lang/rust#56068 and LLVM D74169 [0] if curious. I tested with the aforementioned lld patch (and none of the other changes) and it cut the binary down to 300MB. With the other changes, the savings are less substantial, but probably another 10MB to be had. [0]: https://reviews.llvm.org/D74169
Our Linux release binary was hilariously large, weighing in at nearly 800MB (!). Nearly all of the bloat was from DWARF debug info: $ bloaty materialized -n 10 FILE SIZE VM SIZE -------------- -------------- 24.5% 194Mi 0.0% 0 .debug_info 24.1% 191Mi 0.0% 0 .debug_loc 13.8% 109Mi 0.0% 0 .debug_pubtypes 10.1% 79.9Mi 0.0% 0 .debug_pubnames 8.8% 70.0Mi 0.0% 0 .debug_str 8.3% 66.3Mi 0.0% 0 .debug_ranges 4.4% 35.3Mi 0.0% 0 .debug_line 3.1% 24.8Mi 66.3% 24.8Mi .text 1.8% 14.4Mi 25.1% 9.39Mi [41 Others] 0.6% 4.79Mi 0.0% 0 .strtab 0.4% 3.22Mi 8.6% 3.22Mi .eh_frame 100.0% 793Mi 100.0% 37.4Mi TOTAL This patch gets a handle on this by attacking the problem from several angles: 1. We instruct the linker to compress debug info sections. Most of the debug info is redundant and compresses exceptionally well. Part of the reason we didn't notice the issue is because our Docker images and gzipped tarballs were relatively small (~150MB). 2. We strip out the unnecessary `.debug_pubnames` and `.debug_pubtypes` sections from the binary. This works around a known Rust bug (rust-lang/rust#46034). 3. We ask Rust to generate less debug info for release builds, limiting it to line info. This is enough information to symbolicate a backtrace, but not enough information to run an interactive debugger. This is usually the right tradeoff for a release build. $ bloaty materialized -n 10 FILE SIZE VM SIZE -------------- -------------- 33.8% 31.9Mi 0.0% 0 .debug_info 26.5% 25.0Mi 70.5% 25.0Mi .text 8.0% 7.54Mi 0.0% 0 .debug_str 6.7% 6.36Mi 0.0% 0 .debug_line 5.7% 5.36Mi 9.4% 3.33Mi [38 Others] 5.0% 4.71Mi 0.0% 0 .strtab 3.8% 3.55Mi 0.0% 0 .debug_ranges 3.3% 3.11Mi 8.8% 3.11Mi .eh_frame 3.0% 2.87Mi 0.0% 0 .symtab 2.2% 2.12Mi 6.0% 2.12Mi .rodata 2.0% 1.92Mi 5.4% 1.92Mi .gcc_except_table 100.0% 94.4Mi 100.0% 35.5Mi TOTAL One issue remains unsolved, which is that Rust/LLVM cannot currently garbage collect DWARF that refers to unused symbols/types. The actual symbols get cut from the binary, but their debug info remains. Follow rust-lang/rust#56068 and LLVM D74169 [0] if curious. I tested with the aforementioned lld patch and the resulting binary is even small, at 71MB, so there's another 25MB of savings to be had there. (That patch on its own, without the other changes, cuts the ~800MB binary to a ~300MB binary, so it's an impressive piece of work. Unfortunately it also increases link time by 15-25x.) [0]: https://reviews.llvm.org/D74169
Our Linux release binary was hilariously large, weighing in at nearly 800MB (!). Nearly all of the bloat was from DWARF debug info: $ bloaty materialized -n 10 FILE SIZE VM SIZE -------------- -------------- 24.5% 194Mi 0.0% 0 .debug_info 24.1% 191Mi 0.0% 0 .debug_loc 13.8% 109Mi 0.0% 0 .debug_pubtypes 10.1% 79.9Mi 0.0% 0 .debug_pubnames 8.8% 70.0Mi 0.0% 0 .debug_str 8.3% 66.3Mi 0.0% 0 .debug_ranges 4.4% 35.3Mi 0.0% 0 .debug_line 3.1% 24.8Mi 66.3% 24.8Mi .text 1.8% 14.4Mi 25.1% 9.39Mi [41 Others] 0.6% 4.79Mi 0.0% 0 .strtab 0.4% 3.22Mi 8.6% 3.22Mi .eh_frame 100.0% 793Mi 100.0% 37.4Mi TOTAL This patch gets a handle on this by attacking the problem from several angles: 1. We instruct the linker to compress debug info sections. Most of the debug info is redundant and compresses exceptionally well. Part of the reason we didn't notice the issue is because our Docker images and gzipped tarballs were relatively small (~150MB). 2. We strip out the unnecessary `.debug_pubnames` and `.debug_pubtypes` sections from the binary. This works around a known Rust bug (rust-lang/rust#46034). 3. We ask Rust to generate less debug info for release builds, limiting it to line info. This is enough information to symbolicate a backtrace, but not enough information to run an interactive debugger. This is usually the right tradeoff for a release build. $ bloaty materialized -n 10 FILE SIZE VM SIZE -------------- -------------- 33.8% 31.9Mi 0.0% 0 .debug_info 26.5% 25.0Mi 70.5% 25.0Mi .text 8.0% 7.54Mi 0.0% 0 .debug_str 6.7% 6.36Mi 0.0% 0 .debug_line 5.7% 5.36Mi 9.4% 3.33Mi [38 Others] 5.0% 4.71Mi 0.0% 0 .strtab 3.8% 3.55Mi 0.0% 0 .debug_ranges 3.3% 3.11Mi 8.8% 3.11Mi .eh_frame 3.0% 2.87Mi 0.0% 0 .symtab 2.2% 2.12Mi 6.0% 2.12Mi .rodata 2.0% 1.92Mi 5.4% 1.92Mi .gcc_except_table 100.0% 94.4Mi 100.0% 35.5Mi TOTAL One issue remains unsolved, which is that Rust/LLVM cannot currently garbage collect DWARF that refers to unused symbols/types. The actual symbols get cut from the binary, but their debug info remains. Follow rust-lang/rust#56068 and LLVM D74169 [0] if curious. I tested with the aforementioned lld patch and the resulting binary is even small, at 71MB, so there's another 25MB of savings to be had there. (That patch on its own, without the other changes, cuts the ~800MB binary to a ~300MB binary, so it's an impressive piece of work. Unfortunately it also increases link time by 15-25x.) [0]: https://reviews.llvm.org/D74169
What's the process for stabilizing that flag? It's working great for me, and seems to much better align with expectations of what "no debuginfo" means! |
#71825 (comment) suggests introducing |
Closing in favor of the tracking issue for |
There's also #34651 which is about putting debuginfo into separate files (instead of deleting it completely like |
To be clear, I originally opened this issue to clarify if 10 times larger binary size is expected compared to stripped. |
I think it's expected because the debuginfo is from libstd, so it'll be large. |
Does it include the debuginfo for the whole of std, even though only parts of std are linked in? Is it not possible to only include debuginfo for the linked in parts? |
It's possibly not the whole of std, but it will include extra debuginfo since DWARF is not designed to cheaply use only portions of it (the alternative would be slower links, and links are already too slow). |
Standard hello world program generated with
cargo new --bin mytest
Built with
cargo build --release
, the resulting binary size is 4270408 bytes.This sounds unreasonably large for a hello world program.
After
strip --strip-all
, we get 449224.The non-stripped version is almost 10 times larger than the stripped version. Why?
This is with
rustc 1.23.0-nightly (fa26421f5 2017-11-15)
, installed through rustup.Here is stable
rustc 1.21.0 (3b72af97e 2017-10-09)
for good measure:Normal: 4059968
Stripped: 404096
This time, the initial size is lower, but the stripped size is even smaller, making the non-stripped version slightly more than 10 times larger.
(As a sidenote, I've been noticing that binary sizes for the same program have been steadily increasing with every new rustc, at least on
x86_64-unknown-linux-gnu
, but that's a separate issue, I suppose)The text was updated successfully, but these errors were encountered: