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

debug-fmt-detail option #123940

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

kornelski
Copy link
Contributor

I'd like to propose a new option that makes #[derive(Debug)] generate no-op implementations that don't print anything, and makes {:?} in format strings a no-op.

There are a couple of motivations for this:

  1. A more thorough stripping of debug symbols. Binaries stripped of debug symbols still retain some of them through Debug implementations. It's hard to avoid that without compiler's help, because debug formatting can be used in many places, including dependencies, and their loggers, asserts, panics, etc.
    • In my testing it gives about 2% binary size reduction on top of all other binary-minimizing best practices (including panic_immediate_abort). There are targets like Web WASM or embedded where users pay attention to binary sizes.
    • Users distributing closed-source binaries may not want to "leak" any symbol names as a matter of principle.
  2. Adds ability to test whether code depends on specifics of the Debug format implementation in unwise ways (e.g. trying to get data unavailable via public interface, or using it as a serialization format). Because current Rust's debug implementation doesn't change, there's a risk of it becoming a fragile de-facto API that won't be possible to change in the future. An option that "breaks" it can act as a grease.

This implementation is a -Z debug-fmt-detail=opt flag that takes:

  • full — the default, current state.
  • none — makes derived Debug and {:?} no-ops. Explicit impl Debug for T implementations are left unharmed, but {:?} format won't use them, so they may get dead-code eliminated if they aren't invoked directly.
  • shallow — makes derived Debug print only the type's name, without recursing into fields. Fieldless enums print their variant names. {:?} works.

The shallow option is a compromise between minimizing the Debug code, and compatibility. There are popular proc-macro crates that use Debug::fmt as a way to convert enum values into their Rust source code.

There's a corresponding cfg flag: #[cfg(debug_fmt_detail = "none")] that can be used in user code to react to this setting to minimize custom Debug implementations or remove unnecessary formatting helper functions.

@rustbot
Copy link
Collaborator

rustbot commented Apr 14, 2024

r? @estebank

rustbot has assigned @estebank.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-libs Relevant to the library team, which will review and decide on the PR/issue. labels Apr 14, 2024
@rustbot
Copy link
Collaborator

rustbot commented Apr 14, 2024

Some changes occurred in cfg and check-cfg configuration

cc @Urgau

@rust-log-analyzer

This comment has been minimized.

@kpreid
Copy link
Contributor

kpreid commented Apr 14, 2024

I'd love to see this happen.

I have a suggestion for a refinement: In the none case, I think it might be better to write a constant string rather than the empty string. While this does slightly negate the code-size benefit, it will aid debuggability to get a recognizable string rather than a mysteriously empty one (because many different bugs can cause empty strings, and the empty string might even be used intentionally with the expectation that no value's Debug representation is the empty string).

This constant string should be "[object Object]" — just kidding. Maybe something like "<disabled>", "{debug-fmt-detail=none}", or perhaps, for minimality while still using Rust idiom,"_".

compiler/rustc_session/src/config/cfg.rs Outdated Show resolved Hide resolved
compiler/rustc_session/src/options.rs Outdated Show resolved Hide resolved
@rust-log-analyzer

This comment has been minimized.

@rustbot
Copy link
Collaborator

rustbot commented Apr 15, 2024

Some changes occurred in tests/ui/check-cfg

cc @Urgau

@rust-log-analyzer

This comment has been minimized.

@kornelski
Copy link
Contributor Author

@kpreid But none isn't merely writing an empty string. It makes Debug::fmt an empty function that doesn't touch the Formatter, which makes it possible to optimize it entirely.

There is LoweringContext::lower_format_args that maybe could replace {:?} with a string literal. However, it needs to keep the arg expressions for their side effects. Perhaps the args could be changed to something like drop(&arg) or (&arg, "").1, but I wasn't able to figure out how to transform the expressions. It needs to modify FormatArgs with ast::Expr, but the lower_format_args seems to have access only to hir::Expr.

@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

compiler/rustc_feature/src/builtin_attrs.rs Show resolved Hide resolved
compiler/rustc_session/src/config.rs Outdated Show resolved Hide resolved
compiler/rustc_session/src/config/cfg.rs Outdated Show resolved Hide resolved
compiler/rustc_session/src/config/cfg.rs Outdated Show resolved Hide resolved
compiler/rustc_session/src/config/cfg.rs Outdated Show resolved Hide resolved
@rust-log-analyzer

This comment has been minimized.

@kornelski kornelski force-pushed the remove-derived-debug branch 2 times, most recently from bf36e51 to 3905795 Compare April 20, 2024 15:45
@rustbot
Copy link
Collaborator

rustbot commented Apr 20, 2024

Some changes occurred in src/doc/rustc/src/check-cfg.md

cc @Urgau

@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@bors

This comment was marked as resolved.

@rust-log-analyzer

This comment has been minimized.

@bors

This comment was marked as resolved.

@bors

This comment was marked as resolved.

@bors

This comment was marked as resolved.

@estebank
Copy link
Contributor

@rfcbot fcp merge

@rfcbot
Copy link

rfcbot commented May 21, 2024

Team member @estebank has proposed to merge this. The next step is review by the rest of the tagged team members:

No concerns currently listed.

Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

See this document for info about what commands tagged team members can give me.

@rfcbot rfcbot added proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. labels May 21, 2024
@bors

This comment was marked as resolved.

Allows disabling `fmt::Debug` derive and debug formatting.
@@ -117,6 +117,10 @@ impl<'a> Argument<'a> {
Self::new(x, Debug::fmt)
}
#[inline(always)]
pub fn new_debug_noop<'b, T: Debug>(x: &'b T) -> Argument<'_> {
Self::new(x, |_, _| Ok(()))
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Why are shallow and none implemented differently?
One have a separate function in the library, and another does not and is processed in rustc_builtin_macros.

@petrochenkov
Copy link
Contributor

I suggest dropping the "detail" from the name.
We have similar options like -C debuginfo and they are not called -C debuginfo-level or -C debuginfo-detail, it's already clear from the possible values (and -C already says that it's about codegen).

(Also maybe debug_fmt -> fmt_debug to match the trait/macro path std::fmt::Debug.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-libs Relevant to the library team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

10 participants