Skip to content

Commit 6411fef

Browse files
Properly apply path prefix remapping paths emitted into debuginfo.
1 parent 9e7b0ff commit 6411fef

File tree

9 files changed

+193
-73
lines changed

9 files changed

+193
-73
lines changed

compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs

+95-67
Original file line numberDiff line numberDiff line change
@@ -36,20 +36,21 @@ use rustc_middle::ty::subst::GenericArgKind;
3636
use rustc_middle::ty::{self, AdtKind, Instance, ParamEnv, Ty, TyCtxt, COMMON_VTABLE_ENTRIES};
3737
use rustc_session::config::{self, DebugInfo};
3838
use rustc_span::symbol::Symbol;
39+
use rustc_span::FileName;
3940
use rustc_span::FileNameDisplayPreference;
40-
use rustc_span::{self, SourceFile, SourceFileHash};
41+
use rustc_span::{self, SourceFile};
4142
use rustc_target::abi::{Align, Size};
4243
use smallvec::smallvec;
4344
use tracing::debug;
4445

4546
use libc::{c_longlong, c_uint};
4647
use std::borrow::Cow;
47-
use std::collections::hash_map::Entry;
4848
use std::fmt::{self, Write};
4949
use std::hash::{Hash, Hasher};
5050
use std::iter;
5151
use std::path::{Path, PathBuf};
5252
use std::ptr;
53+
use tracing::instrument;
5354

5455
impl PartialEq for llvm::Metadata {
5556
fn eq(&self, other: &Self) -> bool {
@@ -527,78 +528,105 @@ fn hex_encode(data: &[u8]) -> String {
527528
}
528529

529530
pub fn file_metadata<'ll>(cx: &CodegenCx<'ll, '_>, source_file: &SourceFile) -> &'ll DIFile {
530-
debug!("file_metadata: file_name: {:?}", source_file.name);
531-
532-
let hash = Some(&source_file.src_hash);
533-
let file_name = Some(source_file.name.prefer_remapped().to_string());
534-
let directory = if source_file.is_real_file() && !source_file.is_imported() {
535-
Some(
536-
cx.sess()
537-
.opts
538-
.working_dir
539-
.to_string_lossy(FileNameDisplayPreference::Remapped)
540-
.to_string(),
541-
)
542-
} else {
543-
// If the path comes from an upstream crate we assume it has been made
544-
// independent of the compiler's working directory one way or another.
545-
None
546-
};
547-
file_metadata_raw(cx, file_name, directory, hash)
548-
}
549-
550-
pub fn unknown_file_metadata<'ll>(cx: &CodegenCx<'ll, '_>) -> &'ll DIFile {
551-
file_metadata_raw(cx, None, None, None)
552-
}
553-
554-
fn file_metadata_raw<'ll>(
555-
cx: &CodegenCx<'ll, '_>,
556-
file_name: Option<String>,
557-
directory: Option<String>,
558-
hash: Option<&SourceFileHash>,
559-
) -> &'ll DIFile {
560-
let key = (file_name, directory);
561-
562-
match debug_context(cx).created_files.borrow_mut().entry(key) {
563-
Entry::Occupied(o) => o.get(),
564-
Entry::Vacant(v) => {
565-
let (file_name, directory) = v.key();
566-
debug!("file_metadata: file_name: {:?}, directory: {:?}", file_name, directory);
567-
568-
let file_name = file_name.as_deref().unwrap_or("<unknown>");
569-
let directory = directory.as_deref().unwrap_or("");
570-
571-
let (hash_kind, hash_value) = match hash {
572-
Some(hash) => {
573-
let kind = match hash.kind {
574-
rustc_span::SourceFileHashAlgorithm::Md5 => llvm::ChecksumKind::MD5,
575-
rustc_span::SourceFileHashAlgorithm::Sha1 => llvm::ChecksumKind::SHA1,
576-
rustc_span::SourceFileHashAlgorithm::Sha256 => llvm::ChecksumKind::SHA256,
577-
};
578-
(kind, hex_encode(hash.hash_bytes()))
531+
let cache_key = Some((source_file.name_hash, source_file.src_hash));
532+
return debug_context(cx)
533+
.created_files
534+
.borrow_mut()
535+
.entry(cache_key)
536+
.or_insert_with(|| alloc_new_file_metadata(cx, source_file));
537+
538+
#[instrument(skip(cx, source_file), level = "debug")]
539+
fn alloc_new_file_metadata<'ll>(
540+
cx: &CodegenCx<'ll, '_>,
541+
source_file: &SourceFile,
542+
) -> &'ll DIFile {
543+
debug!(?source_file.name);
544+
545+
let (directory, file_name) = match &source_file.name {
546+
FileName::Real(filename) => {
547+
let working_directory = &cx.sess().opts.working_dir;
548+
debug!(?working_directory);
549+
550+
let filename = cx
551+
.sess()
552+
.source_map()
553+
.path_mapping()
554+
.to_embeddable_absolute_path(filename.clone(), working_directory);
555+
556+
// Construct the absolute path of the file
557+
let abs_path = filename.remapped_path_if_available();
558+
debug!(?abs_path);
559+
560+
if let Ok(rel_path) =
561+
abs_path.strip_prefix(working_directory.remapped_path_if_available())
562+
{
563+
// If the compiler's working directory (which also is the DW_AT_comp_dir of
564+
// the compilation unit) is a prefix of the path we are about to emit, then
565+
// only emit the part relative to the working directory.
566+
// Because of path remapping we sometimes see strange things here: `abs_path`
567+
// might actually look like a relative path
568+
// (e.g. `<crate-name-and-version>/src/lib.rs`), so if we emit it without
569+
// taking the working directory into account, downstream tooling will
570+
// interpret it as `<working-directory>/<crate-name-and-version>/src/lib.rs`,
571+
// which makes no sense. Usually in such cases the working directory will also
572+
// be remapped to `<crate-name-and-version>` or some other prefix of the path
573+
// we are remapping, so we end up with
574+
// `<crate-name-and-version>/<crate-name-and-version>/src/lib.rs`.
575+
// By moving the working directory portion into the `directory` part of the
576+
// DIFile, we allow LLVM to emit just the relative path for DWARF, while
577+
// still emitting the correct absolute path for CodeView.
578+
(
579+
working_directory.to_string_lossy(FileNameDisplayPreference::Remapped),
580+
rel_path.to_string_lossy().into_owned(),
581+
)
582+
} else {
583+
("".into(), abs_path.to_string_lossy().into_owned())
579584
}
580-
None => (llvm::ChecksumKind::None, String::new()),
581-
};
585+
}
586+
other => ("".into(), other.prefer_remapped().to_string_lossy().into_owned()),
587+
};
582588

583-
let file_metadata = unsafe {
584-
llvm::LLVMRustDIBuilderCreateFile(
585-
DIB(cx),
586-
file_name.as_ptr().cast(),
587-
file_name.len(),
588-
directory.as_ptr().cast(),
589-
directory.len(),
590-
hash_kind,
591-
hash_value.as_ptr().cast(),
592-
hash_value.len(),
593-
)
594-
};
589+
let hash_kind = match source_file.src_hash.kind {
590+
rustc_span::SourceFileHashAlgorithm::Md5 => llvm::ChecksumKind::MD5,
591+
rustc_span::SourceFileHashAlgorithm::Sha1 => llvm::ChecksumKind::SHA1,
592+
rustc_span::SourceFileHashAlgorithm::Sha256 => llvm::ChecksumKind::SHA256,
593+
};
594+
let hash_value = hex_encode(source_file.src_hash.hash_bytes());
595595

596-
v.insert(file_metadata);
597-
file_metadata
596+
unsafe {
597+
llvm::LLVMRustDIBuilderCreateFile(
598+
DIB(cx),
599+
file_name.as_ptr().cast(),
600+
file_name.len(),
601+
directory.as_ptr().cast(),
602+
directory.len(),
603+
hash_kind,
604+
hash_value.as_ptr().cast(),
605+
hash_value.len(),
606+
)
598607
}
599608
}
600609
}
601610

611+
pub fn unknown_file_metadata<'ll>(cx: &CodegenCx<'ll, '_>) -> &'ll DIFile {
612+
debug_context(cx).created_files.borrow_mut().entry(None).or_insert_with(|| unsafe {
613+
let file_name = "<unknown>";
614+
let directory = "";
615+
let hash_value = "";
616+
617+
llvm::LLVMRustDIBuilderCreateFile(
618+
DIB(cx),
619+
file_name.as_ptr().cast(),
620+
file_name.len(),
621+
directory.as_ptr().cast(),
622+
directory.len(),
623+
llvm::ChecksumKind::None,
624+
hash_value.as_ptr().cast(),
625+
hash_value.len(),
626+
)
627+
})
628+
}
629+
602630
trait MsvcBasicName {
603631
fn msvc_basic_name(self) -> &'static str;
604632
}

compiler/rustc_codegen_llvm/src/debuginfo/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ use rustc_middle::ty::{self, Instance, ParamEnv, Ty, TypeFoldable};
3131
use rustc_session::config::{self, DebugInfo};
3232
use rustc_session::Session;
3333
use rustc_span::symbol::Symbol;
34-
use rustc_span::{self, BytePos, Pos, SourceFile, SourceFileAndLine, Span};
34+
use rustc_span::{self, BytePos, Pos, SourceFile, SourceFileAndLine, SourceFileHash, Span};
3535
use rustc_target::abi::Size;
3636

3737
use libc::c_uint;
@@ -61,7 +61,7 @@ pub struct CodegenUnitDebugContext<'ll, 'tcx> {
6161
llcontext: &'ll llvm::Context,
6262
llmod: &'ll llvm::Module,
6363
builder: &'ll mut DIBuilder<'ll>,
64-
created_files: RefCell<FxHashMap<(Option<String>, Option<String>), &'ll DIFile>>,
64+
created_files: RefCell<FxHashMap<Option<(u128, SourceFileHash)>, &'ll DIFile>>,
6565

6666
type_map: metadata::TypeMap<'ll, 'tcx>,
6767
namespace_map: RefCell<DefIdMap<&'ll DIScope>>,

compiler/rustc_metadata/src/rmeta/encoder.rs

+7
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,13 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
500500
(!source_file.is_imported() || self.is_proc_macro)
501501
})
502502
.map(|(_, source_file)| {
503+
// At export time we expand all source file paths to absolute paths because
504+
// downstream compilation sessions can have a different compiler working
505+
// directory, so relative paths from this or any other upstream crate
506+
// won't be valid anymore.
507+
//
508+
// At this point we also erase the actual on-disk path and only keep
509+
// the remapped version -- as is necessary for reproducible builds.
503510
match source_file.name {
504511
FileName::Real(ref original_file_name) => {
505512
let adapted_file_name =

compiler/rustc_span/src/lib.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -335,8 +335,8 @@ impl fmt::Display for FileNameDisplay<'_> {
335335
}
336336
}
337337

338-
impl FileNameDisplay<'_> {
339-
pub fn to_string_lossy(&self) -> Cow<'_, str> {
338+
impl<'a> FileNameDisplay<'a> {
339+
pub fn to_string_lossy(&self) -> Cow<'a, str> {
340340
match self.inner {
341341
FileName::Real(ref inner) => inner.to_string_lossy(self.display_pref),
342342
_ => Cow::from(format!("{}", self)),
@@ -1153,7 +1153,7 @@ impl FromStr for SourceFileHashAlgorithm {
11531153
}
11541154

11551155
/// The hash of the on-disk source file used for debug info.
1156-
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
1156+
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
11571157
#[derive(HashStable_Generic, Encodable, Decodable)]
11581158
pub struct SourceFileHash {
11591159
pub kind: SourceFileHashAlgorithm,

compiler/rustc_span/src/source_map.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1099,6 +1099,8 @@ impl FilePathMapping {
10991099
/// the path was affected by the mapping.
11001100
pub fn map_prefix(&self, path: PathBuf) -> (PathBuf, bool) {
11011101
if path.as_os_str().is_empty() {
1102+
// Exit early if the path is empty and therefore there's nothing to remap.
1103+
// This is mostly to reduce spam for `RUSTC_LOG=[remap_path_prefix]`.
11021104
return (path, false);
11031105
}
11041106

src/bootstrap/dist.rs

+1
Original file line numberDiff line numberDiff line change
@@ -2047,6 +2047,7 @@ impl Step for RustDev {
20472047
"llvm-cov",
20482048
"llvm-dwp",
20492049
"llvm-nm",
2050+
"llvm-dwarfdump",
20502051
] {
20512052
tarball.add_file(src_bindir.join(exe(bin, target)), "bin", 0o755);
20522053
}

src/test/codegen/remap_path_prefix/main.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ fn main() {
2222
}
2323

2424
// Here we check that local debuginfo is mapped correctly.
25-
// CHECK: !DIFile(filename: "/the/src/remap_path_prefix/main.rs", directory: "/the/cwd"
25+
// CHECK: !DIFile(filename: "/the/src/remap_path_prefix/main.rs", directory: ""
2626

2727
// And here that debuginfo from other crates are expanded to absolute paths.
2828
// CHECK: !DIFile(filename: "/the/aux-src/remap_path_prefix_aux.rs", directory: ""
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# This test makes sure that --remap-path-prefix has the expected effects on paths in debuginfo.
2+
# It tests several cases, each of them has a detailed description attached to it.
3+
4+
# ignore-windows
5+
6+
SRC_DIR := $(abspath .)
7+
SRC_DIR_PARENT := $(abspath ..)
8+
9+
-include ../../run-make-fulldeps/tools.mk
10+
11+
all: \
12+
abs_input_outside_working_dir \
13+
rel_input_remap_working_dir \
14+
rel_input_remap_working_dir_parent \
15+
rel_input_remap_working_dir_child \
16+
abs_input_inside_working_dir \
17+
abs_input_outside_working_dir
18+
19+
# The compiler is called with an *ABSOLUTE PATH* as input, and that absolute path *is* within
20+
# the working directory of the compiler. We are remapping the path that contains `src`.
21+
abs_input_inside_working_dir:
22+
# We explicitly switch to a directory that *is* a prefix of the directory our
23+
# source code is contained in.
24+
cd $(SRC_DIR) && $(RUSTC) $(SRC_DIR)/src/quux.rs -o "$(TMPDIR)/abs_input_inside_working_dir.rlib" -Cdebuginfo=2 --remap-path-prefix $(SRC_DIR)=REMAPPED
25+
# We expect the path to the main source file to be remapped.
26+
"$(LLVM_BIN_DIR)"/llvm-dwarfdump $(TMPDIR)/abs_input_inside_working_dir.rlib | $(CGREP) "REMAPPED/src/quux.rs"
27+
# No weird duplication of remapped components (see #78479)
28+
"$(LLVM_BIN_DIR)"/llvm-dwarfdump $(TMPDIR)/abs_input_inside_working_dir.rlib | $(CGREP) -v "REMAPPED/REMAPPED"
29+
30+
# The compiler is called with an *ABSOLUTE PATH* as input, and that absolute path is *not* within
31+
# the working directory of the compiler. We are remapping both the path that contains `src` and
32+
# the working directory to the same thing. This setup corresponds to a workaround that is needed
33+
# when trying to remap everything to something that looks like a local path.
34+
# Relative paths are interpreted as relative to the compiler's working directory (e.g. in
35+
# debuginfo). If we also remap the working directory, the compiler strip it from other paths so
36+
# that the final outcome is the desired one again.
37+
abs_input_outside_working_dir:
38+
# We explicitly switch to a directory that is *not* a prefix of the directory our
39+
# source code is contained in.
40+
cd $(TMPDIR) && $(RUSTC) $(SRC_DIR)/src/quux.rs -o "$(TMPDIR)/abs_input_outside_working_dir.rlib" -Cdebuginfo=2 --remap-path-prefix $(SRC_DIR)=REMAPPED --remap-path-prefix $(TMPDIR)=REMAPPED
41+
"$(LLVM_BIN_DIR)"/llvm-dwarfdump $(TMPDIR)/abs_input_outside_working_dir.rlib | $(CGREP) "REMAPPED/src/quux.rs"
42+
# No weird duplication of remapped components (see #78479)
43+
"$(LLVM_BIN_DIR)"/llvm-dwarfdump $(TMPDIR)/abs_input_outside_working_dir.rlib | $(CGREP) -v "REMAPPED/REMAPPED"
44+
45+
# The compiler is called with a *RELATIVE PATH* as input. We are remapping the working directory of
46+
# the compiler, which naturally is an implicit prefix of our relative input path. Debuginfo will
47+
# expand the relative path to an absolute path and we expect the working directory to be remapped
48+
# in that expansion.
49+
rel_input_remap_working_dir:
50+
cd $(SRC_DIR) && $(RUSTC) src/quux.rs -o "$(TMPDIR)/rel_input_remap_working_dir.rlib" -Cdebuginfo=2 --remap-path-prefix "$(SRC_DIR)=REMAPPED"
51+
"$(LLVM_BIN_DIR)"/llvm-dwarfdump "$(TMPDIR)/rel_input_remap_working_dir.rlib" | $(CGREP) "REMAPPED/src/quux.rs"
52+
# No weird duplication of remapped components (see #78479)
53+
"$(LLVM_BIN_DIR)"/llvm-dwarfdump "$(TMPDIR)/rel_input_remap_working_dir.rlib" | $(CGREP) -v "REMAPPED/REMAPPED"
54+
55+
# The compiler is called with a *RELATIVE PATH* as input. We are remapping a *SUB-DIRECTORY* of the
56+
# compiler's working directory. This test makes sure that that directory is remapped even though it
57+
# won't actually show up in this form in the compiler's SourceMap and instead is only constructed
58+
# on demand during debuginfo generation.
59+
rel_input_remap_working_dir_child:
60+
cd $(SRC_DIR) && $(RUSTC) src/quux.rs -o "$(TMPDIR)/rel_input_remap_working_dir_child.rlib" -Cdebuginfo=2 --remap-path-prefix "$(SRC_DIR)/src=REMAPPED"
61+
# We expect `src/quux.rs` to have been remapped to `REMAPPED/quux.rs`.
62+
"$(LLVM_BIN_DIR)"/llvm-dwarfdump "$(TMPDIR)/rel_input_remap_working_dir_child.rlib" | $(CGREP) "REMAPPED/quux.rs"
63+
# We don't want to find the path that we just remapped anywhere in the DWARF
64+
"$(LLVM_BIN_DIR)"/llvm-dwarfdump "$(TMPDIR)/rel_input_remap_working_dir_child.rlib" | $(CGREP) -v "$(SRC_DIR)/src"
65+
# No weird duplication of remapped components (see #78479)
66+
"$(LLVM_BIN_DIR)"/llvm-dwarfdump "$(TMPDIR)/rel_input_remap_working_dir_child.rlib" | $(CGREP) -v "REMAPPED/REMAPPED"
67+
68+
# The compiler is called with a *RELATIVE PATH* as input. We are remapping a *PARENT DIRECTORY* of
69+
# the compiler's working directory.
70+
rel_input_remap_working_dir_parent:
71+
cd $(SRC_DIR) && $(RUSTC) src/quux.rs -o "$(TMPDIR)/rel_input_remap_working_dir_parent.rlib" -Cdebuginfo=2 --remap-path-prefix "$(SRC_DIR_PARENT)=REMAPPED"
72+
# We expect `src/quux.rs` to have been remapped to `REMAPPED/remap-path-prefix-dwarf/src/quux.rs`.
73+
"$(LLVM_BIN_DIR)"/llvm-dwarfdump "$(TMPDIR)/rel_input_remap_working_dir_parent.rlib" | $(CGREP) "REMAPPED/remap-path-prefix-dwarf/src/quux.rs"
74+
# We don't want to find the path that we just remapped anywhere in the DWARF
75+
"$(LLVM_BIN_DIR)"/llvm-dwarfdump "$(TMPDIR)/rel_input_remap_working_dir_parent.rlib" | $(CGREP) -v "$(SRC_DIR_PARENT)"
76+
# No weird duplication of remapped components (see #78479)
77+
"$(LLVM_BIN_DIR)"/llvm-dwarfdump "$(TMPDIR)/rel_input_remap_working_dir_parent.rlib" | $(CGREP) -v "REMAPPED/REMAPPED"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#![crate_type = "rlib"]
2+
3+
pub fn foo() {
4+
println!("foo");
5+
}

0 commit comments

Comments
 (0)