1
Fork 0

sess/cg: re-introduce split dwarf kind

In #79570, `-Z split-dwarf-kind={none,single,split}` was replaced by `-C
split-debuginfo={off,packed,unpacked}`. `-C split-debuginfo`'s packed
and unpacked aren't exact parallels to single and split, respectively.

On Unix, `-C split-debuginfo=packed` will put debuginfo into object
files and package debuginfo into a DWARF package file (`.dwp`) and
`-C split-debuginfo=unpacked` will put debuginfo into dwarf object files
and won't package it.

In the initial implementation of Split DWARF, split mode wrote sections
which did not require relocation into a DWARF object (`.dwo`) file which
was ignored by the linker and then packaged those DWARF objects into
DWARF packages (`.dwp`). In single mode, sections which did not require
relocation were written into object files but ignored by the linker and
were not packaged. However, both split and single modes could be
packaged or not, the primary difference in behaviour was where the
debuginfo sections that did not require link-time relocation were
written (in a DWARF object or the object file).

This commit re-introduces a `-Z split-dwarf-kind` flag, which can be
used to pick between split and single modes when `-C split-debuginfo` is
used to enable Split DWARF (either packed or unpacked).

Signed-off-by: David Wood <david.wood@huawei.com>
This commit is contained in:
David Wood 2021-10-08 16:10:17 +00:00
parent f1ce0e6a00
commit 08ed338f56
8 changed files with 184 additions and 61 deletions

View file

@ -23,7 +23,7 @@ use rustc_errors::{FatalError, Handler, Level};
use rustc_fs_util::{link_or_copy, path_to_c_string}; use rustc_fs_util::{link_or_copy, path_to_c_string};
use rustc_middle::bug; use rustc_middle::bug;
use rustc_middle::ty::TyCtxt; use rustc_middle::ty::TyCtxt;
use rustc_session::config::{self, Lto, OutputType, Passes, SwitchWithOptPath}; use rustc_session::config::{self, Lto, OutputType, Passes, SplitDwarfKind, SwitchWithOptPath};
use rustc_session::Session; use rustc_session::Session;
use rustc_span::symbol::sym; use rustc_span::symbol::sym;
use rustc_span::InnerSpan; use rustc_span::InnerSpan;
@ -106,7 +106,11 @@ 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 { pub fn create_target_machine(tcx: TyCtxt<'_>, mod_name: &str) -> &'static mut llvm::TargetMachine {
let split_dwarf_file = if tcx.sess.target_can_use_split_dwarf() { let split_dwarf_file = if tcx.sess.target_can_use_split_dwarf() {
tcx.output_filenames(()).split_dwarf_path(tcx.sess.split_debuginfo(), Some(mod_name)) tcx.output_filenames(()).split_dwarf_path(
tcx.sess.split_debuginfo(),
tcx.sess.opts.debugging_opts.split_dwarf_kind,
Some(mod_name),
)
} else { } else {
None None
}; };
@ -892,17 +896,18 @@ pub(crate) unsafe fn codegen(
.generic_activity_with_arg("LLVM_module_codegen_emit_obj", &*module.name); .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 = cgcx.output_filenames.temp_path_dwo(module_name);
let dwo_out = match cgcx.split_debuginfo { let dwo_out = match (cgcx.split_debuginfo, cgcx.split_dwarf_kind) {
// Don't change how DWARF is emitted in single mode (or when disabled). // Don't change how DWARF is emitted when disabled.
SplitDebuginfo::Off | SplitDebuginfo::Packed => None, (SplitDebuginfo::Off, _) => None,
// Emit (a subset of the) DWARF into a separate file in split mode. // Don't provide a DWARF object path if split debuginfo is enabled but this is
SplitDebuginfo::Unpacked => { // a platform that doesn't support Split DWARF.
if cgcx.target_can_use_split_dwarf { _ if !cgcx.target_can_use_split_dwarf => None,
Some(dwo_out.as_path()) // Don't provide a DWARF object path in single mode, sections will be written
} else { // into the object as normal but ignored by linker.
None (_, SplitDwarfKind::Single) => None,
} // Emit (a subset of the) DWARF into a separate dwarf object file in split
} // mode.
(_, SplitDwarfKind::Split) => Some(dwo_out.as_path()),
}; };
with_codegen(tm, llmod, config.no_builtins, |cpm| { with_codegen(tm, llmod, config.no_builtins, |cpm| {
@ -939,7 +944,9 @@ pub(crate) unsafe fn codegen(
Ok(module.into_compiled_module( Ok(module.into_compiled_module(
config.emit_obj != EmitObj::None, config.emit_obj != EmitObj::None,
cgcx.target_can_use_split_dwarf && cgcx.split_debuginfo == SplitDebuginfo::Unpacked, cgcx.target_can_use_split_dwarf
&& cgcx.split_debuginfo != SplitDebuginfo::Off
&& cgcx.split_dwarf_kind == SplitDwarfKind::Split,
config.emit_bc, config.emit_bc,
&cgcx.output_filenames, &cgcx.output_filenames,
)) ))

View file

@ -1072,7 +1072,11 @@ pub fn compile_unit_metadata<'ll, 'tcx>(
let output_filenames = tcx.output_filenames(()); let output_filenames = tcx.output_filenames(());
let split_name = if tcx.sess.target_can_use_split_dwarf() { let split_name = if tcx.sess.target_can_use_split_dwarf() {
output_filenames output_filenames
.split_dwarf_path(tcx.sess.split_debuginfo(), Some(codegen_unit_name)) .split_dwarf_path(
tcx.sess.split_debuginfo(),
tcx.sess.opts.debugging_opts.split_dwarf_kind,
Some(codegen_unit_name),
)
// We get a path relative to the working directory from split_dwarf_path // We get a path relative to the working directory from split_dwarf_path
.map(|f| tcx.sess.source_map().path_mapping().map_prefix(f).0) .map(|f| tcx.sess.source_map().path_mapping().map_prefix(f).0)
} else { } else {

View file

@ -5,7 +5,7 @@ use rustc_fs_util::fix_windows_verbatim_for_gcc;
use rustc_hir::def_id::CrateNum; use rustc_hir::def_id::CrateNum;
use rustc_middle::middle::dependency_format::Linkage; use rustc_middle::middle::dependency_format::Linkage;
use rustc_session::config::{self, CFGuard, CrateType, DebugInfo, LdImpl, Strip}; use rustc_session::config::{self, CFGuard, CrateType, DebugInfo, LdImpl, Strip};
use rustc_session::config::{OutputFilenames, OutputType, PrintRequest}; use rustc_session::config::{OutputFilenames, OutputType, PrintRequest, SplitDwarfKind};
use rustc_session::cstore::DllImport; use rustc_session::cstore::DllImport;
use rustc_session::output::{check_file_is_writeable, invalid_output_for_target, out_filename}; use rustc_session::output::{check_file_is_writeable, invalid_output_for_target, out_filename};
use rustc_session::search_paths::PathKind; use rustc_session::search_paths::PathKind;
@ -134,25 +134,20 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>(
} }
} }
// Remove the temporary object file and metadata if we aren't saving temps // Remove the temporary object file and metadata if we aren't saving temps.
sess.time("link_binary_remove_temps", || { sess.time("link_binary_remove_temps", || {
if !sess.opts.cg.save_temps { // If the user requests that temporaries are saved, don't delete any.
if sess.opts.cg.save_temps {
return;
}
let remove_temps_from_module = |module: &CompiledModule| { let remove_temps_from_module = |module: &CompiledModule| {
if let Some(ref obj) = module.object { if let Some(ref obj) = module.object {
ensure_removed(sess.diagnostic(), obj); ensure_removed(sess.diagnostic(), obj);
} }
if let Some(ref obj) = module.dwarf_object {
ensure_removed(sess.diagnostic(), obj);
}
}; };
if sess.opts.output_types.should_link() && !preserve_objects_for_their_debuginfo(sess) { // Otherwise, always remove the metadata and allocator module temporaries.
for module in &codegen_results.modules {
remove_temps_from_module(module);
}
}
if let Some(ref metadata_module) = codegen_results.metadata_module { if let Some(ref metadata_module) = codegen_results.metadata_module {
remove_temps_from_module(metadata_module); remove_temps_from_module(metadata_module);
} }
@ -160,6 +155,27 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>(
if let Some(ref allocator_module) = codegen_results.allocator_module { if let Some(ref allocator_module) = codegen_results.allocator_module {
remove_temps_from_module(allocator_module); remove_temps_from_module(allocator_module);
} }
// If no requested outputs require linking, then the object temporaries should
// be kept.
if !sess.opts.output_types.should_link() {
return;
}
// Potentially keep objects for their debuginfo.
let (preserve_objects, preserve_dwarf_objects) = preserve_objects_for_their_debuginfo(sess);
debug!(?preserve_objects, ?preserve_dwarf_objects);
for module in &codegen_results.modules {
if !preserve_objects {
remove_temps_from_module(module);
}
if !preserve_dwarf_objects {
if let Some(ref obj) = module.dwarf_object {
ensure_removed(sess.diagnostic(), obj);
}
}
} }
}); });
@ -1138,26 +1154,36 @@ pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) {
bug!("Not enough information provided to determine how to invoke the linker"); bug!("Not enough information provided to determine how to invoke the linker");
} }
/// Returns a boolean indicating whether we should preserve the object files on /// Returns a pair of boolean indicating whether we should preserve the object and
/// the filesystem for their debug information. This is often useful with /// dwarf object files on the filesystem for their debug information. This is often
/// split-dwarf like schemes. /// useful with split-dwarf like schemes.
fn preserve_objects_for_their_debuginfo(sess: &Session) -> bool { fn preserve_objects_for_their_debuginfo(sess: &Session) -> (bool, bool) {
// If the objects don't have debuginfo there's nothing to preserve. // If the objects don't have debuginfo there's nothing to preserve.
if sess.opts.debuginfo == config::DebugInfo::None { if sess.opts.debuginfo == config::DebugInfo::None {
return false; return (false, false);
} }
// If we're only producing artifacts that are archives, no need to preserve // If we're only producing artifacts that are archives, no need to preserve
// the objects as they're losslessly contained inside the archives. // the objects as they're losslessly contained inside the archives.
let output_linked = if sess.crate_types().iter().all(|&x| x.is_archive()) {
sess.crate_types().iter().any(|&x| x != CrateType::Rlib && x != CrateType::Staticlib); return (false, false);
if !output_linked {
return false;
} }
// "unpacked" split debuginfo means that we leave object files as the match (sess.split_debuginfo(), sess.opts.debugging_opts.split_dwarf_kind) {
// debuginfo is found in the original object files themselves // If there is no split debuginfo then do not preserve objects.
sess.split_debuginfo() == SplitDebuginfo::Unpacked (SplitDebuginfo::Off, _) => (false, false),
// If there is packed split debuginfo, then the debuginfo in the objects
// has been packaged and the objects can be deleted.
(SplitDebuginfo::Packed, _) => (false, false),
// If there is unpacked split debuginfo and the current target can not use
// split dwarf, then keep objects.
(SplitDebuginfo::Unpacked, _) if !sess.target_can_use_split_dwarf() => (true, false),
// If there is unpacked split debuginfo and the target can use split dwarf, then
// keep the object containing that debuginfo (whether that is an object file or
// dwarf object file depends on the split dwarf kind).
(SplitDebuginfo::Unpacked, SplitDwarfKind::Single) => (true, false),
(SplitDebuginfo::Unpacked, SplitDwarfKind::Split) => (false, true),
}
} }
fn archive_search_paths(sess: &Session) -> Vec<PathBuf> { fn archive_search_paths(sess: &Session) -> Vec<PathBuf> {

View file

@ -286,7 +286,11 @@ impl TargetMachineFactoryConfig {
module_name: &str, module_name: &str,
) -> TargetMachineFactoryConfig { ) -> TargetMachineFactoryConfig {
let split_dwarf_file = if cgcx.target_can_use_split_dwarf { let split_dwarf_file = if cgcx.target_can_use_split_dwarf {
cgcx.output_filenames.split_dwarf_path(cgcx.split_debuginfo, Some(module_name)) cgcx.output_filenames.split_dwarf_path(
cgcx.split_debuginfo,
cgcx.split_dwarf_kind,
Some(module_name),
)
} else { } else {
None None
}; };
@ -329,6 +333,7 @@ pub struct CodegenContext<B: WriteBackendMethods> {
pub target_arch: String, pub target_arch: String,
pub debuginfo: config::DebugInfo, pub debuginfo: config::DebugInfo,
pub split_debuginfo: rustc_target::spec::SplitDebuginfo, pub split_debuginfo: rustc_target::spec::SplitDebuginfo,
pub split_dwarf_kind: rustc_session::config::SplitDwarfKind,
// Number of cgus excluding the allocator/metadata modules // Number of cgus excluding the allocator/metadata modules
pub total_cgus: usize, pub total_cgus: usize,
@ -1060,6 +1065,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
target_arch: tcx.sess.target.arch.clone(), target_arch: tcx.sess.target.arch.clone(),
debuginfo: tcx.sess.opts.debuginfo, debuginfo: tcx.sess.opts.debuginfo,
split_debuginfo: tcx.sess.split_debuginfo(), split_debuginfo: tcx.sess.split_debuginfo(),
split_dwarf_kind: tcx.sess.opts.debugging_opts.split_dwarf_kind,
}; };
// This is the "main loop" of parallel work happening for parallel codegen. // This is the "main loop" of parallel work happening for parallel codegen.

View file

@ -231,6 +231,37 @@ pub enum DebugInfo {
Full, Full,
} }
/// Split debug-information is enabled by `-C split-debuginfo`, this enum is only used if split
/// debug-information is enabled (in either `Packed` or `Unpacked` modes), and the platform
/// uses DWARF for debug-information.
///
/// Some debug-information 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 in DWARF object files, or by 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 {
/// Sections which do not require relocation are written into object file but ignored by the
/// linker.
Single,
/// Sections which do not require relocation are written into a DWARF object (`.dwo`) file
/// which is ignored by the linker.
Split,
}
impl FromStr for SplitDwarfKind {
type Err = ();
fn from_str(s: &str) -> Result<Self, ()> {
Ok(match s {
"single" => SplitDwarfKind::Single,
"split" => SplitDwarfKind::Split,
_ => return Err(()),
})
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)] #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)]
#[derive(Encodable, Decodable)] #[derive(Encodable, Decodable)]
pub enum OutputType { pub enum OutputType {
@ -378,7 +409,7 @@ impl OutputTypes {
self.0.len() self.0.len()
} }
// Returns `true` if any of the output types require codegen or linking. /// Returns `true` if any of the output types require codegen or linking.
pub fn should_codegen(&self) -> bool { pub fn should_codegen(&self) -> bool {
self.0.keys().any(|k| match *k { self.0.keys().any(|k| match *k {
OutputType::Bitcode OutputType::Bitcode
@ -391,7 +422,7 @@ impl OutputTypes {
}) })
} }
// Returns `true` if any of the output types require linking. /// Returns `true` if any of the output types require linking.
pub fn should_link(&self) -> bool { pub fn should_link(&self) -> bool {
self.0.keys().any(|k| match *k { self.0.keys().any(|k| match *k {
OutputType::Bitcode OutputType::Bitcode
@ -681,18 +712,23 @@ impl OutputFilenames {
pub fn split_dwarf_path( pub fn split_dwarf_path(
&self, &self,
split_debuginfo_kind: SplitDebuginfo, split_debuginfo_kind: SplitDebuginfo,
split_dwarf_kind: SplitDwarfKind,
cgu_name: Option<&str>, cgu_name: Option<&str>,
) -> Option<PathBuf> { ) -> Option<PathBuf> {
let obj_out = self.temp_path(OutputType::Object, cgu_name); let obj_out = self.temp_path(OutputType::Object, cgu_name);
let dwo_out = self.temp_path_dwo(cgu_name); let dwo_out = self.temp_path_dwo(cgu_name);
match split_debuginfo_kind { match (split_debuginfo_kind, split_dwarf_kind) {
SplitDebuginfo::Off => None, (SplitDebuginfo::Off, SplitDwarfKind::Single | SplitDwarfKind::Split) => None,
// Single mode doesn't change how DWARF is emitted, but does add Split DWARF attributes // 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 // (pointing at the path which is being determined here). Use the path to the current
// object file. // object file.
SplitDebuginfo::Packed => Some(obj_out), (SplitDebuginfo::Packed | SplitDebuginfo::Unpacked, SplitDwarfKind::Single) => {
Some(obj_out)
}
// Split mode emits the DWARF into a different file, use that path. // Split mode emits the DWARF into a different file, use that path.
SplitDebuginfo::Unpacked => Some(dwo_out), (SplitDebuginfo::Packed | SplitDebuginfo::Unpacked, SplitDwarfKind::Split) => {
Some(dwo_out)
}
} }
} }
} }
@ -821,6 +857,18 @@ pub enum CrateType {
impl_stable_hash_via_hash!(CrateType); impl_stable_hash_via_hash!(CrateType);
impl CrateType {
/// When generated, is this crate type an archive?
pub fn is_archive(&self) -> bool {
match *self {
CrateType::Rlib | CrateType::Staticlib => true,
CrateType::Executable | CrateType::Dylib | CrateType::Cdylib | CrateType::ProcMacro => {
false
}
}
}
}
#[derive(Clone, Hash, Debug, PartialEq, Eq)] #[derive(Clone, Hash, Debug, PartialEq, Eq)]
pub enum Passes { pub enum Passes {
Some(Vec<String>), Some(Vec<String>),

View file

@ -412,6 +412,8 @@ mod desc {
pub const parse_wasi_exec_model: &str = "either `command` or `reactor`"; pub const parse_wasi_exec_model: &str = "either `command` or `reactor`";
pub const parse_split_debuginfo: &str = pub const parse_split_debuginfo: &str =
"one of supported split-debuginfo modes (`off`, `packed`, or `unpacked`)"; "one of supported split-debuginfo modes (`off`, `packed`, or `unpacked`)";
pub const parse_split_dwarf_kind: &str =
"one of supported split dwarf modes (`split` or `single`)";
pub const parse_gcc_ld: &str = "one of: no value, `lld`"; pub const parse_gcc_ld: &str = "one of: no value, `lld`";
pub const parse_stack_protector: &str = pub const parse_stack_protector: &str =
"one of (`none` (default), `basic`, `strong`, or `all`)"; "one of (`none` (default), `basic`, `strong`, or `all`)";
@ -941,6 +943,14 @@ mod parse {
true true
} }
crate fn parse_split_dwarf_kind(slot: &mut SplitDwarfKind, v: Option<&str>) -> bool {
match v.and_then(|s| SplitDwarfKind::from_str(s).ok()) {
Some(e) => *slot = e,
_ => return false,
}
true
}
crate fn parse_gcc_ld(slot: &mut Option<LdImpl>, v: Option<&str>) -> bool { crate fn parse_gcc_ld(slot: &mut Option<LdImpl>, v: Option<&str>) -> bool {
match v { match v {
None => *slot = None, None => *slot = None,
@ -1403,6 +1413,14 @@ options! {
"control stack smash protection strategy (`rustc --print stack-protector-strategies` for details)"), "control stack smash protection strategy (`rustc --print stack-protector-strategies` for details)"),
strip: Strip = (Strip::None, parse_strip, [UNTRACKED], strip: Strip = (Strip::None, parse_strip, [UNTRACKED],
"tell the linker which information to strip (`none` (default), `debuginfo` or `symbols`)"), "tell the linker which information to strip (`none` (default), `debuginfo` or `symbols`)"),
split_dwarf_kind: SplitDwarfKind = (SplitDwarfKind::Split, parse_split_dwarf_kind, [UNTRACKED],
"split dwarf variant (only if -Csplit-debuginfo is enabled and on relevant platform)
(default: `split`)
`split`: sections which do not require relocation are written into a DWARF object (`.dwo`)
file which is ignored by the linker
`single`: sections which do not require relocation are written into object file but ignored
by the linker"),
split_dwarf_inlining: bool = (true, parse_bool, [UNTRACKED], split_dwarf_inlining: bool = (true, parse_bool, [UNTRACKED],
"provide minimal debug info in the object/executable to facilitate online \ "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"), symbolication/stack traces in the absence of .dwo/.dwp files when using Split DWARF"),

View file

@ -479,7 +479,7 @@ pub enum SplitDebuginfo {
/// ///
/// * Windows - not supported /// * Windows - not supported
/// * macOS - supported, scattered object files /// * macOS - supported, scattered object files
/// * ELF - supported, scattered `*.dwo` files /// * ELF - supported, scattered `*.dwo` or `*.o` files (see `SplitDwarfKind`)
Unpacked, Unpacked,
} }

View file

@ -44,16 +44,30 @@ off:
[ ! -f $(TMPDIR)/*.dwp ] [ ! -f $(TMPDIR)/*.dwp ]
[ ! -f $(TMPDIR)/*.dwo ] [ ! -f $(TMPDIR)/*.dwo ]
packed: packed: packed-split packed-single
$(RUSTC) foo.rs -g -C split-debuginfo=packed -Z unstable-options
packed-split:
$(RUSTC) foo.rs -g -C split-debuginfo=packed -Z unstable-options -Zsplit-dwarf-kind=split
ls $(TMPDIR)/*.dwp
rm -rf $(TMPDIR)/*.dwp $(TMPDIR)/*.dwo
packed-single:
$(RUSTC) foo.rs -g -C split-debuginfo=packed -Z unstable-options -Zsplit-dwarf-kind=single
ls $(TMPDIR)/*.dwp ls $(TMPDIR)/*.dwp
ls $(TMPDIR)/*.dwo && exit 1 || exit 0 ls $(TMPDIR)/*.dwo && exit 1 || exit 0
rm -rf $(TMPDIR)/*.dwp rm -rf $(TMPDIR)/*.dwp
unpacked: unpacked: unpacked-split unpacked-single
$(RUSTC) foo.rs -g -C split-debuginfo=unpacked -Z unstable-options
unpacked-split:
$(RUSTC) foo.rs -g -C split-debuginfo=unpacked -Z unstable-options -Zsplit-dwarf-kind=split
ls $(TMPDIR)/*.dwp && exit 1 || exit 0 ls $(TMPDIR)/*.dwp && exit 1 || exit 0
ls $(TMPDIR)/*.dwo ls $(TMPDIR)/*.dwo
rm -rf $(TMPDIR)/*.dwo rm -rf $(TMPDIR)/*.dwp $(TMPDIR)/*.dwo
unpacked-single:
$(RUSTC) foo.rs -g -C split-debuginfo=unpacked -Z unstable-options -Zsplit-dwarf-kind=single
ls $(TMPDIR)/*.dwp && exit 1 || exit 0
ls $(TMPDIR)/*.dwo && exit 1 || exit 0
endif endif
endif endif