rustc: Stabilize `-Zrun-dsymutil` as `-Csplit-debuginfo`

This commit adds a new stable codegen option to rustc,
`-Csplit-debuginfo`. The old `-Zrun-dsymutil` flag is deleted and now
subsumed by this stable flag. Additionally `-Zsplit-dwarf` is also
subsumed by this flag but still requires `-Zunstable-options` to
actually activate. The `-Csplit-debuginfo` flag takes one of
three values:

* `off` - This indicates that split-debuginfo from the final artifact is
  not desired. This is not supported on Windows and is the default on
  Unix platforms except macOS. On macOS this means that `dsymutil` is
  not executed.

* `packed` - This means that debuginfo is desired in one location
  separate from the main executable. This is the default on Windows
  (`*.pdb`) and macOS (`*.dSYM`). On other Unix platforms this subsumes
  `-Zsplit-dwarf=single` and produces a `*.dwp` file.

* `unpacked` - This means that debuginfo will be roughly equivalent to
  object files, meaning that it's throughout the build directory
  rather than in one location (often the fastest for local development).
  This is not the default on any platform and is not supported on Windows.

Each target can indicate its own default preference for how debuginfo is
handled. Almost all platforms default to `off` except for Windows and
macOS which default to `packed` for historical reasons.

Some equivalencies for previous unstable flags with the new flags are:

* `-Zrun-dsymutil=yes` -> `-Csplit-debuginfo=packed`
* `-Zrun-dsymutil=no` -> `-Csplit-debuginfo=unpacked`
* `-Zsplit-dwarf=single` -> `-Csplit-debuginfo=packed`
* `-Zsplit-dwarf=split` -> `-Csplit-debuginfo=unpacked`

Note that `-Csplit-debuginfo` still requires `-Zunstable-options` for
non-macOS platforms since split-dwarf support was *just* implemented in
rustc.

There's some more rationale listed on #79361, but the main gist of the
motivation for this commit is that `dsymutil` can take quite a long time
to execute in debug builds and provides little benefit. This means that
incremental compile times appear that much worse on macOS because the
compiler is constantly running `dsymutil` over every single binary it
produces during `cargo build` (even build scripts!). Ideally rustc would
switch to not running `dsymutil` by default, but that's a problem left
to get tackled another day.

Closes #79361
This commit is contained in:
Alex Crichton 2020-11-30 08:39:08 -08:00
parent 7fba12bb1d
commit a124043fb0
21 changed files with 352 additions and 134 deletions

View File

@ -732,10 +732,7 @@ pub unsafe fn optimize_thin_module(
let diag_handler = cgcx.create_diag_handler();
let module_name = &thin_module.shared.module_names[thin_module.idx];
let split_dwarf_file = cgcx
.output_filenames
.split_dwarf_filename(cgcx.split_dwarf_kind, Some(module_name.to_str().unwrap()));
let tm_factory_config = TargetMachineFactoryConfig { split_dwarf_file };
let tm_factory_config = TargetMachineFactoryConfig::new(cgcx, module_name.to_str().unwrap());
let tm =
(cgcx.tm_factory)(tm_factory_config).map_err(|e| write::llvm_err(&diag_handler, &e))?;

View File

@ -23,13 +23,11 @@ use rustc_fs_util::{link_or_copy, path_to_c_string};
use rustc_hir::def_id::LOCAL_CRATE;
use rustc_middle::bug;
use rustc_middle::ty::TyCtxt;
use rustc_session::config::{
self, Lto, OutputType, Passes, SanitizerSet, SplitDwarfKind, SwitchWithOptPath,
};
use rustc_session::config::{self, Lto, OutputType, Passes, SanitizerSet, SwitchWithOptPath};
use rustc_session::Session;
use rustc_span::symbol::sym;
use rustc_span::InnerSpan;
use rustc_target::spec::{CodeModel, RelocModel};
use rustc_target::spec::{CodeModel, RelocModel, SplitDebuginfo};
use tracing::debug;
use libc::{c_char, c_int, c_uint, c_void, size_t};
@ -93,9 +91,12 @@ pub fn create_informational_target_machine(sess: &Session) -> &'static mut llvm:
}
pub fn create_target_machine(tcx: TyCtxt<'_>, mod_name: &str) -> &'static mut llvm::TargetMachine {
let split_dwarf_file = tcx
.output_filenames(LOCAL_CRATE)
.split_dwarf_filename(tcx.sess.opts.debugging_opts.split_dwarf, Some(mod_name));
let split_dwarf_file = if tcx.sess.target_can_use_split_dwarf() {
tcx.output_filenames(LOCAL_CRATE)
.split_dwarf_filename(tcx.sess.split_debuginfo(), Some(mod_name))
} else {
None
};
let config = TargetMachineFactoryConfig { split_dwarf_file };
target_machine_factory(&tcx.sess, tcx.backend_optimization_level(LOCAL_CRATE))(config)
.unwrap_or_else(|err| llvm_err(tcx.sess.diagnostic(), &err).raise())
@ -838,11 +839,17 @@ pub(crate) unsafe fn codegen(
.generic_activity_with_arg("LLVM_module_codegen_emit_obj", &module.name[..]);
let dwo_out = cgcx.output_filenames.temp_path_dwo(module_name);
let dwo_out = match cgcx.split_dwarf_kind {
let dwo_out = match cgcx.split_debuginfo {
// Don't change how DWARF is emitted in single mode (or when disabled).
SplitDwarfKind::None | SplitDwarfKind::Single => None,
SplitDebuginfo::Off | SplitDebuginfo::Packed => None,
// Emit (a subset of the) DWARF into a separate file in split mode.
SplitDwarfKind::Split => Some(dwo_out.as_path()),
SplitDebuginfo::Unpacked => {
if cgcx.target_can_use_split_dwarf {
Some(dwo_out.as_path())
} else {
None
}
}
};
with_codegen(tm, llmod, config.no_builtins, |cpm| {
@ -880,7 +887,7 @@ pub(crate) unsafe fn codegen(
Ok(module.into_compiled_module(
config.emit_obj != EmitObj::None,
cgcx.split_dwarf_kind == SplitDwarfKind::Split,
cgcx.target_can_use_split_dwarf && cgcx.split_debuginfo == SplitDebuginfo::Unpacked,
config.emit_bc,
&cgcx.output_filenames,
))

View File

@ -996,10 +996,13 @@ pub fn compile_unit_metadata(
let flags = "\0";
let out_dir = &tcx.output_filenames(LOCAL_CRATE).out_directory;
let split_name = tcx
.output_filenames(LOCAL_CRATE)
.split_dwarf_filename(tcx.sess.opts.debugging_opts.split_dwarf, Some(codegen_unit_name))
.unwrap_or_default();
let split_name = if tcx.sess.target_can_use_split_dwarf() {
tcx.output_filenames(LOCAL_CRATE)
.split_dwarf_filename(tcx.sess.split_debuginfo(), Some(codegen_unit_name))
} else {
None
}
.unwrap_or_default();
let out_dir = out_dir.to_str().unwrap();
let split_name = split_name.to_str().unwrap();

View File

@ -351,12 +351,7 @@ impl ModuleLlvm {
unsafe {
let llcx = llvm::LLVMRustContextCreate(cgcx.fewer_names);
let llmod_raw = back::lto::parse_module(llcx, name, buffer, handler)?;
let split_dwarf_file = cgcx
.output_filenames
.split_dwarf_filename(cgcx.split_dwarf_kind, Some(name.to_str().unwrap()));
let tm_factory_config = TargetMachineFactoryConfig { split_dwarf_file };
let tm_factory_config = TargetMachineFactoryConfig::new(&cgcx, name.to_str().unwrap());
let tm = match (cgcx.tm_factory)(tm_factory_config) {
Ok(m) => m,
Err(e) => {

View File

@ -14,7 +14,7 @@ use rustc_session::utils::NativeLibKind;
use rustc_session::{filesearch, Session};
use rustc_span::symbol::Symbol;
use rustc_target::spec::crt_objects::{CrtObjects, CrtObjectsFallback};
use rustc_target::spec::{LinkOutputKind, LinkerFlavor, LldFlavor};
use rustc_target::spec::{LinkOutputKind, LinkerFlavor, LldFlavor, SplitDebuginfo};
use rustc_target::spec::{PanicStrategy, RelocModel, RelroLevel, Target};
use super::archive::ArchiveBuilder;
@ -99,9 +99,6 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>(
path.as_ref(),
target_cpu,
);
if sess.opts.debugging_opts.split_dwarf == config::SplitDwarfKind::Split {
link_dwarf_object(sess, &out_filename);
}
}
}
if sess.opts.json_artifact_notifications {
@ -828,29 +825,43 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>(
}
}
// On macOS, debuggers need this utility to get run to do some munging of
// the symbols. Note, though, that if the object files are being preserved
// for their debug information there's no need for us to run dsymutil.
if sess.target.is_like_osx
&& sess.opts.debuginfo != DebugInfo::None
&& !preserve_objects_for_their_debuginfo(sess)
{
let prog = Command::new("dsymutil").arg(out_filename).output();
match prog {
Ok(prog) => {
if !prog.status.success() {
let mut output = prog.stderr.clone();
output.extend_from_slice(&prog.stdout);
sess.struct_warn(&format!(
"processing debug info with `dsymutil` failed: {}",
prog.status
))
.note(&escape_string(&output))
.emit();
match sess.split_debuginfo() {
// If split debug information is disabled or located in individual files
// there's nothing to do here.
SplitDebuginfo::Off | SplitDebuginfo::Unpacked => {}
// If packed split-debuginfo is requested, but the final compilation
// doesn't actually have any debug information, then we skip this step.
SplitDebuginfo::Packed if sess.opts.debuginfo == DebugInfo::None => {}
// On macOS the external `dsymutil` tool is used to create the packed
// debug information. Note that this will read debug information from
// the objects on the filesystem which we'll clean up later.
SplitDebuginfo::Packed if sess.target.is_like_osx => {
let prog = Command::new("dsymutil").arg(out_filename).output();
match prog {
Ok(prog) => {
if !prog.status.success() {
let mut output = prog.stderr.clone();
output.extend_from_slice(&prog.stdout);
sess.struct_warn(&format!(
"processing debug info with `dsymutil` failed: {}",
prog.status
))
.note(&escape_string(&output))
.emit();
}
}
Err(e) => sess.fatal(&format!("unable to run `dsymutil`: {}", e)),
}
Err(e) => sess.fatal(&format!("unable to run `dsymutil`: {}", e)),
}
// On MSVC packed debug information is produced by the linker itself so
// there's no need to do anything else here.
SplitDebuginfo::Packed if sess.target.is_like_msvc => {}
// ... and otherwise we're processing a `*.dwp` packed dwarf file.
SplitDebuginfo::Packed => link_dwarf_object(sess, &out_filename),
}
}
@ -1050,28 +1061,9 @@ fn preserve_objects_for_their_debuginfo(sess: &Session) -> bool {
return false;
}
// Single mode keeps debuginfo in the same object file, but in such a way that it it skipped
// by the linker - so it's expected that when codegen units are linked together that this
// debuginfo would be lost without keeping around the temps.
if sess.opts.debugging_opts.split_dwarf == config::SplitDwarfKind::Single {
return true;
}
// If we're on OSX then the equivalent of split dwarf is turned on by
// default. The final executable won't actually have any debug information
// except it'll have pointers to elsewhere. Historically we've always run
// `dsymutil` to "link all the dwarf together" but this is actually sort of
// a bummer for incremental compilation! (the whole point of split dwarf is
// that you don't do this sort of dwarf link).
//
// Basically as a result this just means that if we're on OSX and we're
// *not* running dsymutil then the object files are the only source of truth
// for debug information, so we must preserve them.
if sess.target.is_like_osx {
return !sess.opts.debugging_opts.run_dsymutil;
}
false
// "unpacked" split debuginfo means that we leave object files as the
// debuginfo is found in the original object files themselves
sess.split_debuginfo() == SplitDebuginfo::Unpacked
}
pub fn archive_search_paths(sess: &Session) -> Vec<PathBuf> {

View File

@ -282,6 +282,20 @@ pub struct TargetMachineFactoryConfig {
pub split_dwarf_file: Option<PathBuf>,
}
impl TargetMachineFactoryConfig {
pub fn new(
cgcx: &CodegenContext<impl WriteBackendMethods>,
module_name: &str,
) -> TargetMachineFactoryConfig {
let split_dwarf_file = if cgcx.target_can_use_split_dwarf {
cgcx.output_filenames.split_dwarf_filename(cgcx.split_debuginfo, Some(module_name))
} else {
None
};
TargetMachineFactoryConfig { split_dwarf_file }
}
}
pub type TargetMachineFactoryFn<B> = Arc<
dyn Fn(TargetMachineFactoryConfig) -> Result<<B as WriteBackendMethods>::TargetMachine, String>
+ Send
@ -311,10 +325,11 @@ pub struct CodegenContext<B: WriteBackendMethods> {
pub tm_factory: TargetMachineFactoryFn<B>,
pub msvc_imps_needed: bool,
pub is_pe_coff: bool,
pub target_can_use_split_dwarf: bool,
pub target_pointer_width: u32,
pub target_arch: String,
pub debuginfo: config::DebugInfo,
pub split_dwarf_kind: config::SplitDwarfKind,
pub split_debuginfo: rustc_target::spec::SplitDebuginfo,
// Number of cgus excluding the allocator/metadata modules
pub total_cgus: usize,
@ -1035,10 +1050,11 @@ fn start_executing_work<B: ExtraBackendMethods>(
total_cgus,
msvc_imps_needed: msvc_imps_needed(tcx),
is_pe_coff: tcx.sess.target.is_like_windows,
target_can_use_split_dwarf: tcx.sess.target_can_use_split_dwarf(),
target_pointer_width: tcx.sess.target.pointer_width,
target_arch: tcx.sess.target.arch.clone(),
debuginfo: tcx.sess.opts.debuginfo,
split_dwarf_kind: tcx.sess.opts.debugging_opts.split_dwarf,
split_debuginfo: tcx.sess.split_debuginfo(),
};
// This is the "main loop" of parallel work happening for parallel codegen.

View File

@ -17,7 +17,7 @@ use rustc_span::edition::{Edition, DEFAULT_EDITION};
use rustc_span::symbol::sym;
use rustc_span::SourceFileHashAlgorithm;
use rustc_target::spec::{CodeModel, LinkerFlavor, MergeFunctions, PanicStrategy};
use rustc_target::spec::{RelocModel, RelroLevel, TlsModel};
use rustc_target::spec::{RelocModel, RelroLevel, SplitDebuginfo, TlsModel};
use std::collections::{BTreeMap, BTreeSet};
use std::iter::FromIterator;
use std::path::PathBuf;
@ -446,6 +446,7 @@ fn test_codegen_options_tracking_hash() {
tracked!(profile_use, Some(PathBuf::from("abc")));
tracked!(relocation_model, Some(RelocModel::Pic));
tracked!(soft_float, true);
tracked!(split_debuginfo, Some(SplitDebuginfo::Packed));
tracked!(target_cpu, Some(String::from("abc")));
tracked!(target_feature, String::from("all the features, all of them"));
}
@ -579,7 +580,6 @@ fn test_debugging_options_tracking_hash() {
tracked!(relax_elf_relocations, Some(true));
tracked!(relro_level, Some(RelroLevel::Full));
tracked!(report_delayed_bugs, true);
tracked!(run_dsymutil, false);
tracked!(sanitizer, SanitizerSet::ADDRESS);
tracked!(sanitizer_memory_track_origins, 2);
tracked!(sanitizer_recover, SanitizerSet::ADDRESS);

View File

@ -13,7 +13,7 @@ use rustc_data_structures::impl_stable_hash_via_hash;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_target::abi::{Align, TargetDataLayout};
use rustc_target::spec::{Target, TargetTriple};
use rustc_target::spec::{SplitDebuginfo, Target, TargetTriple};
use crate::parse::CrateConfig;
use rustc_feature::UnstableFeatures;
@ -221,23 +221,6 @@ pub enum DebugInfo {
Full,
}
/// Some debuginfo requires link-time relocation and some does not. LLVM can partition the debuginfo
/// into sections depending on whether or not it requires link-time relocation. Split DWARF
/// provides a mechanism which allows the linker to skip the sections which don't require link-time
/// relocation - either by putting those sections into DWARF object files, or keeping them in the
/// object file in such a way that the linker will skip them.
#[derive(Clone, Copy, Debug, PartialEq, Hash)]
pub enum SplitDwarfKind {
/// Disabled.
None,
/// Sections which do not require relocation are written into the object file but ignored
/// by the linker.
Single,
/// Sections which do not require relocation are written into a DWARF object (`.dwo`) file,
/// which is skipped by the linker by virtue of being a different file.
Split,
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)]
#[derive(Encodable, Decodable)]
pub enum OutputType {
@ -635,10 +618,10 @@ impl OutputFilenames {
/// mode is being used, which is the logic that this function is intended to encapsulate.
pub fn split_dwarf_filename(
&self,
split_dwarf_kind: SplitDwarfKind,
split_debuginfo_kind: SplitDebuginfo,
cgu_name: Option<&str>,
) -> Option<PathBuf> {
self.split_dwarf_path(split_dwarf_kind, cgu_name)
self.split_dwarf_path(split_debuginfo_kind, cgu_name)
.map(|path| path.strip_prefix(&self.out_directory).unwrap_or(&path).to_path_buf())
}
@ -646,19 +629,19 @@ impl OutputFilenames {
/// mode is being used, which is the logic that this function is intended to encapsulate.
pub fn split_dwarf_path(
&self,
split_dwarf_kind: SplitDwarfKind,
split_debuginfo_kind: SplitDebuginfo,
cgu_name: Option<&str>,
) -> Option<PathBuf> {
let obj_out = self.temp_path(OutputType::Object, cgu_name);
let dwo_out = self.temp_path_dwo(cgu_name);
match split_dwarf_kind {
SplitDwarfKind::None => None,
match split_debuginfo_kind {
SplitDebuginfo::Off => None,
// Single mode doesn't change how DWARF is emitted, but does add Split DWARF attributes
// (pointing at the path which is being determined here). Use the path to the current
// object file.
SplitDwarfKind::Single => Some(obj_out),
SplitDebuginfo::Packed => Some(obj_out),
// Split mode emits the DWARF into a different file, use that path.
SplitDwarfKind::Split => Some(dwo_out),
SplitDebuginfo::Unpacked => Some(dwo_out),
}
}
}
@ -1910,6 +1893,15 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
let pretty = parse_pretty(matches, &debugging_opts, error_format);
if !debugging_opts.unstable_options
&& !target_triple.triple().contains("apple")
&& cg.split_debuginfo.is_some()
{
{
early_error(error_format, "`-Csplit-debuginfo` is unstable on this platform");
}
}
Options {
crate_types,
optimize: opt_level,
@ -2191,7 +2183,7 @@ crate mod dep_tracking {
use rustc_feature::UnstableFeatures;
use rustc_span::edition::Edition;
use rustc_target::spec::{CodeModel, MergeFunctions, PanicStrategy, RelocModel};
use rustc_target::spec::{RelroLevel, TargetTriple, TlsModel};
use rustc_target::spec::{RelroLevel, SplitDebuginfo, TargetTriple, TlsModel};
use std::collections::hash_map::DefaultHasher;
use std::collections::BTreeMap;
use std::hash::Hash;
@ -2263,6 +2255,7 @@ crate mod dep_tracking {
impl_dep_tracking_hash_via_hash!(TargetTriple);
impl_dep_tracking_hash_via_hash!(Edition);
impl_dep_tracking_hash_via_hash!(LinkerPluginLto);
impl_dep_tracking_hash_via_hash!(Option<SplitDebuginfo>);
impl_dep_tracking_hash_via_hash!(SwitchWithOptPath);
impl_dep_tracking_hash_via_hash!(Option<SymbolManglingVersion>);
impl_dep_tracking_hash_via_hash!(Option<SourceFileHashAlgorithm>);

View File

@ -6,7 +6,7 @@ use crate::search_paths::SearchPath;
use crate::utils::NativeLibKind;
use rustc_target::spec::{CodeModel, LinkerFlavor, MergeFunctions, PanicStrategy};
use rustc_target::spec::{RelocModel, RelroLevel, TargetTriple, TlsModel};
use rustc_target::spec::{RelocModel, RelroLevel, SplitDebuginfo, TargetTriple, TlsModel};
use rustc_feature::UnstableFeatures;
use rustc_span::edition::Edition;
@ -269,7 +269,6 @@ macro_rules! options {
pub const parse_switch_with_opt_path: &str =
"an optional path to the profiling data output directory";
pub const parse_merge_functions: &str = "one of: `disabled`, `trampolines`, or `aliases`";
pub const parse_split_dwarf_kind: &str = "one of: `none`, `single` or `split`";
pub const parse_symbol_mangling_version: &str = "either `legacy` or `v0` (RFC 2603)";
pub const parse_src_file_hash: &str = "either `md5` or `sha1`";
pub const parse_relocation_model: &str =
@ -280,6 +279,8 @@ macro_rules! options {
"one of supported TLS models (`rustc --print tls-models`)";
pub const parse_target_feature: &str = parse_string;
pub const parse_wasi_exec_model: &str = "either `command` or `reactor`";
pub const parse_split_debuginfo: &str =
"one of supported split-debuginfo modes (`off` or `dsymutil`)";
}
#[allow(dead_code)]
@ -678,19 +679,6 @@ macro_rules! options {
true
}
fn parse_split_dwarf_kind(
slot: &mut SplitDwarfKind,
v: Option<&str>,
) -> bool {
*slot = match v {
Some("none") => SplitDwarfKind::None,
Some("split") => SplitDwarfKind::Split,
Some("single") => SplitDwarfKind::Single,
_ => return false,
};
true
}
fn parse_symbol_mangling_version(
slot: &mut Option<SymbolManglingVersion>,
v: Option<&str>,
@ -732,6 +720,14 @@ macro_rules! options {
}
true
}
fn parse_split_debuginfo(slot: &mut Option<SplitDebuginfo>, v: Option<&str>) -> bool {
match v.and_then(|s| SplitDebuginfo::from_str(s).ok()) {
Some(e) => *slot = Some(e),
_ => return false,
}
true
}
}
) }
@ -830,6 +826,8 @@ options! {CodegenOptions, CodegenSetter, basic_codegen_options,
"save all temporary output files during compilation (default: no)"),
soft_float: bool = (false, parse_bool, [TRACKED],
"use soft float ABI (*eabihf targets only) (default: no)"),
split_debuginfo: Option<SplitDebuginfo> = (None, parse_split_debuginfo, [TRACKED],
"how to handle split-debuginfo, a platform-specific option"),
target_cpu: Option<String> = (None, parse_opt_string, [TRACKED],
"select target processor (`rustc --print target-cpus` for details)"),
target_feature: String = (String::new(), parse_target_feature, [TRACKED],
@ -1073,11 +1071,6 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
"choose which RELRO level to use"),
report_delayed_bugs: bool = (false, parse_bool, [TRACKED],
"immediately print bugs registered with `delay_span_bug` (default: no)"),
// The default historical behavior was to always run dsymutil, so we're
// preserving that temporarily, but we're likely to switch the default
// soon.
run_dsymutil: bool = (true, parse_bool, [TRACKED],
"if on Mac, run `dsymutil` and delete intermediate object files (default: yes)"),
sanitizer: SanitizerSet = (SanitizerSet::empty(), parse_sanitizers, [TRACKED],
"use a sanitizer"),
sanitizer_memory_track_origins: usize = (0, parse_sanitizer_memory_track_origins, [TRACKED],
@ -1112,8 +1105,6 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
"hash algorithm of source files in debug info (`md5`, `sha1`, or `sha256`)"),
strip: Strip = (Strip::None, parse_strip, [UNTRACKED],
"tell the linker which information to strip (`none` (default), `debuginfo` or `symbols`)"),
split_dwarf: SplitDwarfKind = (SplitDwarfKind::None, parse_split_dwarf_kind, [UNTRACKED],
"enable generation of split dwarf"),
split_dwarf_inlining: bool = (true, parse_bool, [UNTRACKED],
"provide minimal debug info in the object/executable to facilitate online \
symbolication/stack traces in the absence of .dwo/.dwp files when using Split DWARF"),

View File

@ -28,7 +28,7 @@ use rustc_span::source_map::{FileLoader, MultiSpan, RealFileLoader, SourceMap, S
use rustc_span::{sym, SourceFileHashAlgorithm, Symbol};
use rustc_target::asm::InlineAsmArch;
use rustc_target::spec::{CodeModel, PanicStrategy, RelocModel, RelroLevel};
use rustc_target::spec::{Target, TargetTriple, TlsModel};
use rustc_target::spec::{SplitDebuginfo, Target, TargetTriple, TlsModel};
use std::cell::{self, RefCell};
use std::env;
@ -804,6 +804,14 @@ impl Session {
)
}
pub fn split_debuginfo(&self) -> SplitDebuginfo {
self.opts.cg.split_debuginfo.unwrap_or(self.target.split_debuginfo)
}
pub fn target_can_use_split_dwarf(&self) -> bool {
!self.target.is_like_windows && !self.target.is_like_osx
}
pub fn must_not_eliminate_frame_pointers(&self) -> bool {
// "mcount" function relies on stack pointer.
// See <https://sourceware.org/binutils/docs/gprof/Implementation.html>.

View File

@ -1,6 +1,6 @@
use std::env;
use crate::spec::{LinkArgs, TargetOptions};
use crate::spec::{LinkArgs, SplitDebuginfo, TargetOptions};
pub fn opts(os: &str) -> TargetOptions {
// ELF TLS is only available in macOS 10.7+. If you try to compile for 10.6
@ -36,6 +36,10 @@ pub fn opts(os: &str) -> TargetOptions {
emit_debug_gdb_scripts: false,
eh_frame_header: false,
// The historical default for macOS targets is to run `dsymutil` which
// generates a packed version of debuginfo split from the main file.
split_debuginfo: SplitDebuginfo::Packed,
// This environment variable is pretty magical but is intended for
// producing deterministic builds. This was first discovered to be used
// by the `ar` tool as a way to control whether or not mtime entries in

View File

@ -448,6 +448,69 @@ impl fmt::Display for LinkOutputKind {
pub type LinkArgs = BTreeMap<LinkerFlavor, Vec<String>>;
#[derive(Clone, Copy, Hash, Debug, PartialEq, Eq)]
pub enum SplitDebuginfo {
/// Split debug-information is disabled, meaning that on supported platforms
/// you can find all debug information in the executable itself. This is
/// only supported for ELF effectively.
///
/// * Windows - not supported
/// * macOS - don't run `dsymutil`
/// * ELF - `.dwarf_*` sections
Off,
/// Split debug-information can be found in a "packed" location separate
/// from the final artifact. This is supported on all platforms.
///
/// * Windows - `*.pdb`
/// * macOS - `*.dSYM` (run `dsymutil`)
/// * ELF - `*.dwp` (run `rust-llvm-dwp`)
Packed,
/// Split debug-information can be found in individual object files on the
/// filesystem. The main executable may point to the object files.
///
/// * Windows - not supported
/// * macOS - supported, scattered object files
/// * ELF - supported, scattered `*.dwo` files
Unpacked,
}
impl SplitDebuginfo {
fn as_str(&self) -> &'static str {
match self {
SplitDebuginfo::Off => "off",
SplitDebuginfo::Packed => "packed",
SplitDebuginfo::Unpacked => "unpacked",
}
}
}
impl FromStr for SplitDebuginfo {
type Err = ();
fn from_str(s: &str) -> Result<SplitDebuginfo, ()> {
Ok(match s {
"off" => SplitDebuginfo::Off,
"unpacked" => SplitDebuginfo::Unpacked,
"packed" => SplitDebuginfo::Packed,
_ => return Err(()),
})
}
}
impl ToJson for SplitDebuginfo {
fn to_json(&self) -> Json {
self.as_str().to_json()
}
}
impl fmt::Display for SplitDebuginfo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
macro_rules! supported_targets {
( $(($( $triple:literal, )+ $module:ident ),)+ ) => {
$(mod $module;)+
@ -1085,6 +1148,10 @@ pub struct TargetOptions {
/// Is true if the target is an ARM architecture using thumb v1 which allows for
/// thumb and arm interworking.
pub has_thumb_interworking: bool,
/// How to handle split debug information, if at all. Specifying `None` has
/// target-specific meaning.
pub split_debuginfo: SplitDebuginfo,
}
impl Default for TargetOptions {
@ -1184,6 +1251,7 @@ impl Default for TargetOptions {
use_ctors_section: false,
eh_frame_header: true,
has_thumb_interworking: false,
split_debuginfo: SplitDebuginfo::Off,
}
}
}
@ -1382,6 +1450,18 @@ impl Target {
Some(Ok(()))
})).unwrap_or(Ok(()))
} );
($key_name:ident, SplitDebuginfo) => ( {
let name = (stringify!($key_name)).replace("_", "-");
obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| {
match s.parse::<SplitDebuginfo>() {
Ok(level) => base.$key_name = level,
_ => return Some(Err(format!("'{}' is not a valid value for \
split-debuginfo. Use 'off' or 'dsymutil'.",
s))),
}
Some(Ok(()))
})).unwrap_or(Ok(()))
} );
($key_name:ident, list) => ( {
let name = (stringify!($key_name)).replace("_", "-");
if let Some(v) = obj.find(&name).and_then(Json::as_array) {
@ -1627,6 +1707,7 @@ impl Target {
key!(use_ctors_section, bool);
key!(eh_frame_header, bool);
key!(has_thumb_interworking, bool);
key!(split_debuginfo, SplitDebuginfo)?;
// NB: The old name is deprecated, but support for it is retained for
// compatibility.
@ -1862,6 +1943,7 @@ impl ToJson for Target {
target_option_val!(use_ctors_section);
target_option_val!(eh_frame_header);
target_option_val!(has_thumb_interworking);
target_option_val!(split_debuginfo);
if default.unsupported_abis != self.unsupported_abis {
d.insert(

View File

@ -1,4 +1,4 @@
use crate::spec::{LinkArgs, LinkerFlavor, LldFlavor, TargetOptions};
use crate::spec::{LinkArgs, LinkerFlavor, LldFlavor, SplitDebuginfo, TargetOptions};
pub fn opts() -> TargetOptions {
let pre_link_args_msvc = vec![
@ -27,6 +27,10 @@ pub fn opts() -> TargetOptions {
abi_return_struct_as_int: true,
emit_debug_gdb_scripts: false,
// Currently this is the only supported method of debuginfo on MSVC
// where `*.pdb` files show up next to the final artifact.
split_debuginfo: SplitDebuginfo::Packed,
..Default::default()
}
}

View File

@ -1139,10 +1139,18 @@ impl<'a> Builder<'a> {
// itself, we skip it by default since we know it's safe to do so in that case.
// See https://github.com/rust-lang/rust/issues/79361 for more info on this flag.
if target.contains("apple") {
if self.config.rust_run_dsymutil {
rustflags.arg("-Zrun-dsymutil=yes");
if stage == 0 {
if self.config.rust_run_dsymutil {
rustflags.arg("-Zrun-dsymutil=yes");
} else {
rustflags.arg("-Zrun-dsymutil=no");
}
} else {
rustflags.arg("-Zrun-dsymutil=no");
if self.config.rust_run_dsymutil {
rustflags.arg("-Csplit-debuginfo=packed");
} else {
rustflags.arg("-Csplit-debuginfo=unpacked");
}
}
}

View File

@ -492,6 +492,34 @@ point instructions in software. It takes one of the following values:
* `y`, `yes`, `on`, or no value: use soft floats.
* `n`, `no`, or `off`: use hardware floats (the default).
## split-debuginfo
This option controls the emission of "split debuginfo" for debug information
that `rustc` generates. The default behavior of this option is
platform-specific, and not all possible values for this option work on all
platform. Possible values are:
* `off` - This is the default for platforms with ELF binaries and windows-gnu
(not Windows MSVC and not macOS). This typically means that dwarf debug
information can be found in the final artifact in sections of the executable.
This option is not supported on Windows MSVC. On macOS this options prevents
the final execution of `dsymutil` to generate debuginfo.
* `packed` - This is the default for Windows MSVC and macOS platforms. The term
"packed" here means that all the debug information is packed into a separate
file from the main executable. On Windows MSVC this is a `*.pdb` file, on
macOS this is a `*.dSYM` folder, and on other platforms this is a `*.dwp`
files.
* `unpacked` - This means that debug information will be found in separate
files for each compilation unit (object file). This is not supported on
Windows MSVC. On macOS this means the original object files will contain
debug information. On other Unix platforms this means that `*.dwo` files will
contain debug information.
Note that `packed` and `unpacked` gated behind `-Zunstable-options` on
non-macOS platforms at this time.
## target-cpu
This instructs `rustc` to generate code specifically for a particular processor.
@ -499,7 +527,7 @@ This instructs `rustc` to generate code specifically for a particular processor.
You can run `rustc --print target-cpus` to see the valid options to pass
here. Each target has a default base CPU. Special values include:
* `native` can be passed to use the processor of the host machine.
* `native` can be passed to use the processor of the host machine.
* `generic` refers to an LLVM target with minimal features but modern tuning.
## target-feature

View File

@ -0,0 +1,59 @@
-include ../tools.mk
all: off packed unpacked
ifeq ($(UNAME),Darwin)
# If disabled, don't run dsymutil
off:
rm -rf $(TMPDIR)/*.dSYM
$(RUSTC) foo.rs -g -C split-debuginfo=off
[ ! -d $(TMPDIR)/foo.dSYM ]
# Packed by default, but only if debuginfo is requested
packed:
rm -rf $(TMPDIR)/*.dSYM
$(RUSTC) foo.rs
[ ! -d $(TMPDIR)/foo.dSYM ]
rm -rf $(TMPDIR)/*.dSYM
$(RUSTC) foo.rs -g
[ -d $(TMPDIR)/foo.dSYM ]
rm -rf $(TMPDIR)/*.dSYM
$(RUSTC) foo.rs -g -C split-debuginfo=packed
[ -d $(TMPDIR)/foo.dSYM ]
rm -rf $(TMPDIR)/*.dSYM
# Object files are preserved with unpacked and `dsymutil` isn't run
unpacked:
$(RUSTC) foo.rs -g -C split-debuginfo=unpacked
ls $(TMPDIR)/*.o
[ ! -d $(TMPDIR)/foo.dSYM ]
else
ifdef IS_WINDOWS
# Windows only supports =off
off:
packed:
unpacked:
else
# If disabled, don't run dsymutil
off:
$(RUSTC) foo.rs -g -C split-debuginfo=off -Z unstable-options
[ ! -f $(TMPDIR)/*.dwp ]
[ ! -f $(TMPDIR)/*.dwo ]
$(RUSTC) foo.rs -g
[ ! -f $(TMPDIR)/*.dwp ]
[ ! -f $(TMPDIR)/*.dwo ]
packed:
$(RUSTC) foo.rs -g -C split-debuginfo=packed -Z unstable-options
ls $(TMPDIR)/*.dwp
ls $(TMPDIR)/*.dwo && exit 1 || exit 0
rm -rf $(TMPDIR)/*.dwp
unpacked:
$(RUSTC) foo.rs -g -C split-debuginfo=unpacked -Z unstable-options
ls $(TMPDIR)/*.dwp && exit 1 || exit 0
ls $(TMPDIR)/*.dwo
rm -rf $(TMPDIR)/*.dwo
endif
endif

View File

@ -0,0 +1 @@
fn main() {}

View File

@ -3,6 +3,6 @@
# only-linux
all:
$(RUSTC) -Z split-dwarf=split foo.rs
$(RUSTC) -Z unstable-options -C split-debuginfo=packed foo.rs -g
rm $(TMPDIR)/foo.dwp
rm $(TMPDIR)/$(call BIN,foo)

View File

@ -0,0 +1,30 @@
// run-pass
// compile-flags:-g -Csplit-debuginfo=unpacked
// only-macos
#![feature(backtrace)]
use std::process::Command;
use std::str;
#[inline(never)]
fn main() {
let args: Vec<String> = std::env::args().collect();
if args.len() >= 2 {
println!("{}", std::backtrace::Backtrace::force_capture());
return;
}
let out = Command::new(&args[0]).env("RUST_BACKTRACE", "1").arg("foo").output().unwrap();
let output = format!(
"{}\n{}",
str::from_utf8(&out.stdout).unwrap(),
str::from_utf8(&out.stderr).unwrap(),
);
if out.status.success() && output.contains(file!()) {
return;
}
println!("status: {}", out.status);
println!("child output:\n\t{}", output.replace("\n", "\n\t"));
panic!("failed to find {:?} in output", file!());
}

View File

@ -2015,10 +2015,10 @@ impl<'test> TestCx<'test> {
rustc.args(&["-Zchalk"]);
}
Some(CompareMode::SplitDwarf) => {
rustc.args(&["-Zsplit-dwarf=split"]);
rustc.args(&["-Csplit-debuginfo=unpacked", "-Zunstable-options"]);
}
Some(CompareMode::SplitDwarfSingle) => {
rustc.args(&["-Zsplit-dwarf=single"]);
rustc.args(&["-Csplit-debuginfo=packed", "-Zunstable-options"]);
}
None => {}
}

View File

@ -7,7 +7,7 @@ use std::path::Path;
const ENTRY_LIMIT: usize = 1000;
// FIXME: The following limits should be reduced eventually.
const ROOT_ENTRY_LIMIT: usize = 1458;
const ROOT_ENTRY_LIMIT: usize = 1459;
const ISSUES_ENTRY_LIMIT: usize = 2669;
fn check_entries(path: &Path, bad: &mut bool) {