blob: 12b0d5ec4963bd4b5e8203b28a00d9d9d812919c [file] [log] [blame] [edit]
//! Line info generation (`.debug_line`)
use std::ffi::OsStr;
use std::path::{Component, Path};
use cranelift_codegen::MachSrcLoc;
use cranelift_codegen::binemit::CodeOffset;
use gimli::write::{FileId, FileInfo, LineProgram, LineString, LineStringTable};
use rustc_span::{
FileName, Pos, RemapPathScopeComponents, SourceFile, SourceFileAndLine,
SourceFileHashAlgorithm, hygiene,
};
use crate::debuginfo::FunctionDebugContext;
use crate::debuginfo::emit::address_for_func;
use crate::prelude::*;
// OPTIMIZATION: It is cheaper to do this in one pass than using `.parent()` and `.file_name()`.
fn split_path_dir_and_file(path: &Path) -> (&Path, &OsStr) {
let mut iter = path.components();
let file_name = match iter.next_back() {
Some(Component::Normal(p)) => p,
component => {
panic!(
"Path component {:?} of path {} is an invalid filename",
component,
path.display()
);
}
};
let parent = iter.as_path();
(parent, file_name)
}
// OPTIMIZATION: Avoid UTF-8 validation on UNIX.
fn osstr_as_utf8_bytes(path: &OsStr) -> &[u8] {
#[cfg(unix)]
{
use std::os::unix::ffi::OsStrExt;
path.as_bytes()
}
#[cfg(not(unix))]
{
path.to_str().unwrap().as_bytes()
}
}
fn make_file_info(source_file: &SourceFile, embed_source: bool) -> Option<FileInfo> {
let has_md5 = source_file.src_hash.kind == SourceFileHashAlgorithm::Md5;
let has_source = embed_source && source_file.src.is_some();
if !has_md5 && !has_source {
return None;
}
let mut info = FileInfo::default();
if has_md5 {
info.md5.copy_from_slice(source_file.src_hash.hash_bytes());
}
if embed_source {
if let Some(src) = &source_file.src {
info.source = Some(LineString::String(src.as_bytes().to_vec()));
}
}
Some(info)
}
impl DebugContext {
pub(crate) fn get_span_loc(
&mut self,
tcx: TyCtxt<'_>,
function_span: Span,
span: Span,
) -> (FileId, u64, u64) {
// Match behavior of `FunctionCx::adjusted_span_and_dbg_scope`.
let span = hygiene::walk_chain_collapsed(span, function_span);
match tcx.sess.source_map().lookup_line(span.lo()) {
Ok(SourceFileAndLine { sf: file, line }) => {
let file_id = self.add_source_file(&file);
let line_pos = file.lines()[line];
let col = file.relative_position(span.lo()) - line_pos;
(file_id, u64::try_from(line).unwrap() + 1, u64::from(col.to_u32()) + 1)
}
Err(file) => (self.add_source_file(&file), 0, 0),
}
}
pub(crate) fn add_source_file(&mut self, source_file: &SourceFile) -> FileId {
let cache_key = (source_file.stable_id, source_file.src_hash);
*self.created_files.entry(cache_key).or_insert_with(|| {
let line_program: &mut LineProgram = &mut self.dwarf.unit.line_program;
let line_strings: &mut LineStringTable = &mut self.dwarf.line_strings;
match &source_file.name {
FileName::Real(path) => {
let (dir_path, file_name) =
split_path_dir_and_file(path.path(RemapPathScopeComponents::DEBUGINFO));
let dir_name = osstr_as_utf8_bytes(dir_path.as_os_str());
let file_name = osstr_as_utf8_bytes(file_name);
let dir_id = if !dir_name.is_empty() {
let dir_name =
LineString::new(dir_name, line_program.encoding(), line_strings);
line_program.add_directory(dir_name)
} else {
line_program.default_directory()
};
let file_name =
LineString::new(file_name, line_program.encoding(), line_strings);
let info = make_file_info(source_file, self.embed_source);
let has_md5 = source_file.src_hash.kind == SourceFileHashAlgorithm::Md5;
line_program.file_has_md5 &= has_md5;
line_program.add_file(file_name, dir_id, info)
}
filename => {
// For anonymous sources, create an empty directory instead of using the default
let dir_id = line_program.default_directory();
let dummy_file_name = LineString::new(
filename.prefer_remapped_unconditionally().to_string().into_bytes(),
line_program.encoding(),
line_strings,
);
line_program.add_file(dummy_file_name, dir_id, None)
}
}
})
}
}
impl FunctionDebugContext {
pub(crate) fn add_dbg_loc(&mut self, file_id: FileId, line: u64, column: u64) -> SourceLoc {
let (index, _) = self.source_loc_set.insert_full((file_id, line, column));
SourceLoc::new(u32::try_from(index).unwrap())
}
pub(super) fn create_debug_lines(
&mut self,
debug_context: &mut DebugContext,
func_id: FuncId,
context: &Context,
) -> CodeOffset {
let create_row_for_span =
|debug_context: &mut DebugContext, source_loc: (FileId, u64, u64)| {
let (file_id, line, col) = source_loc;
debug_context.dwarf.unit.line_program.row().file = file_id;
debug_context.dwarf.unit.line_program.row().line = line;
debug_context.dwarf.unit.line_program.row().column = col;
debug_context.dwarf.unit.line_program.generate_row();
};
debug_context.dwarf.unit.line_program.begin_sequence(Some(address_for_func(func_id)));
let mut func_end = 0;
let mcr = context.compiled_code().unwrap();
for &MachSrcLoc { start, end, loc } in mcr.buffer.get_srclocs_sorted() {
debug_context.dwarf.unit.line_program.row().address_offset = u64::from(start);
if !loc.is_default() {
let source_loc = self.source_loc_set[loc.bits() as usize];
create_row_for_span(debug_context, source_loc);
} else {
create_row_for_span(debug_context, self.function_source_loc);
}
func_end = end;
}
debug_context.dwarf.unit.line_program.end_sequence(u64::from(func_end));
let func_end = mcr.buffer.total_size();
assert_ne!(func_end, 0);
func_end
}
}