EDIT: oh, ok, so I guess it's because strip is "debuginfo" here, rather than "true".
I know floats are full of scary corner cases, but… Assuming tens of bytes per an if statement, hundreds of corner cases just in formatting? Is it really that bad?
[profile.release]
strip = true
opt-level = "z"
lto = true
codegen-units = 1
panic = "abort"
cargo +nightly build -Z build-std=std,panic_abort -Z build-std-features=panic_immediate_abort --target x86_64-unknown-linux-gnu --releaseAs mentioned in another thread, I've simply followed https://github.com/johnthagen/min-sized-rust
These are still okay, but then `panic = "abort"` shakes a lot of code off by making the final executable unable to print a nice stack trace (even without symbols) and instead immediately traps/abort()-s.
Edit: and GP also used "panic-immediate-abort", which also removes the dependency to std::fmt::format!() because it now silently abort()s without even printing an error string.
It's comparable to C++ land -fno-exceptions (not exactly, but similar).
Ah, and it should be also noted that some non-fatal signals were also delivered only via panic. The best-known example is a memory allocation failure, which is recoverable in Rust but needed unwinding for a long time. Nowadays you have an unsafe but non-unwinding alternative.
[1] https://blog.rust-lang.org/2020/12/11/lock-poisoning-survey....
I just tried this. The stack traces on panic seem more or less the same with or without panic = "abort" in Cargo.toml.
For example, this program:
fn main() {
let v = vec![1, 2, 3];
v[99];
}
Compiled with panic="abort" outputs this stack trace: $ RUST_BACKTRACE=1 cargo run --release
Compiling rust-panic v0.1.0 (/Users/seph/temp/rust-panic)
Finished release [optimized] target(s) in 1.82s
Running `target/release/rust-panic`
thread 'main' panicked at src/main.rs:4:6:
index out of bounds: the len is 3 but the index is 99
stack backtrace:
0: _rust_begin_unwind
1: core::panicking::panic_fmt
2: core::panicking::panic_bounds_check
3: <alloc::vec::Vec<T,A> as core::ops::index::Index<I>>::index
4: rust_panic::main
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
[1] 95405 abort RUST_BACKTRACE=1 cargo run --release
Weirdly, in this test if I don't strip the binary, I get a larger binary size with panic="abort" than when I leave that out. That is surely due to a bug somewhere.One can get binaries pretty damn small (low-mid tens of kilobytes for a basic cli program doing something like hashing of a file).
Problem I've found with manually compiling std (which has ancillary benefits of being able to compile to a specific uarch) is it can break the compilation process when bringing in third-party deps. The config.toml (stored in $PROJECT_ROOT/.cargo) overrides cargo's behaviour for all dependencies as well - which may break those compilations.
Tbh, it's one reason I don't particularly rate the rustc+cargo toolchain - but for most people writing regular applications: just being able to do ```cargo build -r``` and not care about binary size, uarch optimization or custom llvm/rustc optimizations (PGO etc), most won't care.
I believe it does print backtraces then terminate, since the backtrace is printed via panic hooks, which happen before the actual unwinding.
lto and codegen-units=1 have a huge compile time cost. For release for general distribution you should tend to favour them, but the release profile isn’t just about that activity (especially because debug/opt-level<2 is often just too slow to use while developing). You commonly want to create another profile for production distribution.
Abort on panic changes runtime behaviour by stopping you from catching panics, which will completely break some programs, and harm the failure mode of others, so that e.g. one defective route on a web server will suddenly take the entire website down for everyone (or, if you have a supervisor that can restart the server, at least disrupt it for everyone).
IME optimize for speed vs size usually isn't as clear cut as the name says though, sometimes smaller code does indeed run faster, but in most cases I've seen there's not much of a difference between -O3, -O2, -Os and -Oz.
• `panic = "abort"` means that any panic terminates the program. This is not always desirable because you may want to catch and recover from panics, particularly in long-running servers.
• `strip = true` means that anything depending on DWARF would no longer work. Backtraces won't work, but also unwinding will no longer work (so this is disastrous if you haven't set `panic` above). The actual proposal has `strip = "debuginfo"` instead, so unwinding will work while backtraces won't.
• `codegen-units = 1` is the number of concurrent compilation jobs (cgu) in the LLVM codegen phase. A single cgu will significantly increase the compilation time, while allowing a bit more optimization. Otherwise this is okay.
• `lto = true` enables Rust-specific link-time optimizations across crates. The actual benefit depends on the set of crates linked, but it is significantly slower that many large enough projects wouldn't want it. It does benefit small programs like the "Hello, world" program the most though.
• `opt-level = "z"` is same to C/C++ `-Oz` and the same pros and cons apply.
[1] https://doc.rust-lang.org/cargo/reference/profiles.html#rele...
> This is not always desirable because you may want to catch and recover from panics, particularly in long-running servers.
IMHO a panic implies that execution cannot continue under any circumstances, and even any attempts for a graceful shutdown might be futile (if recovery is possible it shouldn't be a panic but done through regular error handling).
For a server process the best reaction to a panic would mean abort and clean restart.
> A single cgu will significantly increase the compilation time, while allowing a bit more optimization.
Increased build time is acceptable for release mode IMHO.
You shouldn't use them as a general exception mechanism. They aren't the same, even if under the hood they both use stack unwinding.
I suppose you could have a panic in one thread whilst others are still running fine. You may just want to shut those other threads down gracefully.
Rust panic is just a C++ exception in its implementation, and not every C++ programmer would terminate a process when an exception is thrown. Of course Rust panic is more resillient because Rust provides a memory safety and the logic error can be reasonably bounded as a result.
> Increased build time is acceptable for release mode IMHO.
I think the slowest possible configuration is at least 3x slower than the default, and that's too slow to be acceptable for most people. But you can always tune them up if you want---please note that this issue is all about defaults.
That's often infeasible, and creates a major DoS/reliability problem.
The server may be processing hundreds of different requests at the same time, and panic=abort will kill all of them. That creates a visible failure to other unrelated clients of the server, not just the offending request.
If you try to fix that and retry aborted requests, you'll retry the panic-inducing request and cause another failure (and it'll take several restarts to bisect out the offending request).
Plus a restart may be costly, require loading data, warming up caches, etc.
It's just way cheaper to catch a panic and return 500 to the offending request. Rust guarantees to panic before anything terrible memory-corrupting happens. Even if you don't trust it and would prefer to restart anyway, you have an option to gracefully hand over the traffic before the restart.