2019-12-16 13:23:41 +01:00
|
|
|
use std::ffi::OsStr;
|
2020-04-18 16:16:17 +03:00
|
|
|
use std::convert::TryFrom;
|
2019-12-16 13:23:41 +01:00
|
|
|
use std::path::{Component, Path};
|
|
|
|
|
2019-11-12 21:08:08 +01:00
|
|
|
use crate::prelude::*;
|
|
|
|
|
2020-04-18 14:56:04 +03:00
|
|
|
use rustc_span::{FileName, SourceFile, SourceFileAndLine, Pos, SourceFileHash, SourceFileHashAlgorithm};
|
2019-11-12 21:08:08 +01:00
|
|
|
|
2019-12-24 12:40:18 +01:00
|
|
|
use cranelift_codegen::binemit::CodeOffset;
|
2019-11-12 21:10:51 +01:00
|
|
|
|
2019-11-12 21:08:08 +01:00
|
|
|
use gimli::write::{
|
2020-04-18 14:56:04 +03:00
|
|
|
Address, AttributeValue, FileId, LineProgram, LineString, FileInfo, LineStringTable, UnitEntryId,
|
2019-11-12 21:08:08 +01:00
|
|
|
};
|
|
|
|
|
2019-12-16 13:23:41 +01:00
|
|
|
// 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;
|
|
|
|
return path.as_bytes();
|
|
|
|
}
|
|
|
|
#[cfg(not(unix))] {
|
|
|
|
return path.to_str().unwrap().as_bytes();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-18 16:16:17 +03:00
|
|
|
pub(crate) const MD5_LEN: usize = 16;
|
|
|
|
|
|
|
|
#[derive(Default, Clone, Copy)]
|
|
|
|
pub struct FileHash([u8; MD5_LEN]);
|
|
|
|
|
|
|
|
impl FileHash {
|
|
|
|
pub fn inner(self) -> [u8; MD5_LEN] {
|
|
|
|
self.0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct UnsupportedHashType;
|
|
|
|
|
|
|
|
impl TryFrom<SourceFileHash> for FileHash {
|
|
|
|
type Error = UnsupportedHashType;
|
|
|
|
|
|
|
|
fn try_from(hash: SourceFileHash) -> Result<Self, Self::Error> {
|
|
|
|
if hash.kind == SourceFileHashAlgorithm::Md5 {
|
|
|
|
let mut buf = [0u8; MD5_LEN];
|
|
|
|
buf.copy_from_slice(hash.hash_bytes());
|
|
|
|
Ok(Self(buf))
|
|
|
|
} else {
|
|
|
|
Err(UnsupportedHashType)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-12 21:08:08 +01:00
|
|
|
fn line_program_add_file(
|
|
|
|
line_program: &mut LineProgram,
|
|
|
|
line_strings: &mut LineStringTable,
|
2020-04-18 14:56:04 +03:00
|
|
|
file: &SourceFile,
|
2019-11-12 21:08:08 +01:00
|
|
|
) -> FileId {
|
2020-04-18 14:56:04 +03:00
|
|
|
match &file.name {
|
2019-11-12 21:08:08 +01:00
|
|
|
FileName::Real(path) => {
|
2019-12-16 13:23:41 +01:00
|
|
|
let (dir_path, file_name) = split_path_dir_and_file(path);
|
|
|
|
let dir_name = osstr_as_utf8_bytes(dir_path.as_os_str());
|
|
|
|
let file_name = osstr_as_utf8_bytes(file_name);
|
|
|
|
|
2019-11-12 21:08:08 +01:00
|
|
|
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(
|
2019-12-16 13:23:41 +01:00
|
|
|
file_name,
|
2019-11-12 21:08:08 +01:00
|
|
|
line_program.encoding(),
|
|
|
|
line_strings,
|
|
|
|
);
|
2020-04-18 14:56:04 +03:00
|
|
|
|
2020-04-18 16:16:17 +03:00
|
|
|
let file_hash = FileHash::try_from(file.src_hash);
|
2020-04-18 14:56:04 +03:00
|
|
|
|
2020-04-18 16:16:17 +03:00
|
|
|
line_program.file_has_md5 = file_hash.is_ok();
|
2020-04-18 14:56:04 +03:00
|
|
|
line_program.add_file(file_name, dir_id, Some(FileInfo {
|
2020-04-18 16:02:02 +03:00
|
|
|
timestamp: 0,
|
2020-04-18 14:56:04 +03:00
|
|
|
size: 0,
|
2020-04-18 16:16:17 +03:00
|
|
|
md5: file_hash.unwrap_or_default().inner(),
|
2020-04-18 14:56:04 +03:00
|
|
|
}))
|
2019-11-12 21:08:08 +01:00
|
|
|
}
|
|
|
|
// FIXME give more appropriate file names
|
2020-04-18 14:56:04 +03:00
|
|
|
filename => {
|
2019-11-12 21:08:08 +01:00
|
|
|
let dir_id = line_program.default_directory();
|
|
|
|
let dummy_file_name = LineString::new(
|
2020-04-18 14:56:04 +03:00
|
|
|
filename.to_string().into_bytes(),
|
2019-11-12 21:08:08 +01:00
|
|
|
line_program.encoding(),
|
|
|
|
line_strings,
|
|
|
|
);
|
|
|
|
line_program.add_file(dummy_file_name, dir_id, None)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'tcx> DebugContext<'tcx> {
|
|
|
|
pub(super) fn emit_location(&mut self, entry_id: UnitEntryId, span: Span) {
|
2020-02-03 21:36:36 +01:00
|
|
|
let loc = self.tcx.sess.source_map().lookup_char_pos(span.lo());
|
2019-11-12 21:08:08 +01:00
|
|
|
|
|
|
|
let file_id = line_program_add_file(
|
|
|
|
&mut self.dwarf.unit.line_program,
|
|
|
|
&mut self.dwarf.line_strings,
|
2020-04-18 14:56:04 +03:00
|
|
|
&loc.file,
|
2019-11-12 21:08:08 +01:00
|
|
|
);
|
|
|
|
|
|
|
|
let entry = self.dwarf.unit.get_mut(entry_id);
|
|
|
|
|
|
|
|
entry.set(
|
|
|
|
gimli::DW_AT_decl_file,
|
|
|
|
AttributeValue::FileIndex(Some(file_id)),
|
|
|
|
);
|
|
|
|
entry.set(
|
|
|
|
gimli::DW_AT_decl_line,
|
2020-02-03 21:36:36 +01:00
|
|
|
AttributeValue::Udata(loc.line as u64),
|
2019-11-12 21:08:08 +01:00
|
|
|
);
|
|
|
|
// FIXME: probably omit this
|
|
|
|
entry.set(
|
|
|
|
gimli::DW_AT_decl_column,
|
2020-02-03 21:36:36 +01:00
|
|
|
AttributeValue::Udata(loc.col.to_usize() as u64),
|
2019-11-12 21:08:08 +01:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, 'tcx> FunctionDebugContext<'a, 'tcx> {
|
2020-01-14 17:11:06 +01:00
|
|
|
pub(super) fn create_debug_lines(
|
2019-11-12 21:08:08 +01:00
|
|
|
&mut self,
|
|
|
|
context: &Context,
|
2019-12-24 12:40:18 +01:00
|
|
|
isa: &dyn cranelift_codegen::isa::TargetIsa,
|
2019-12-24 12:44:07 +01:00
|
|
|
source_info_set: &indexmap::IndexSet<SourceInfo>,
|
2019-11-12 21:10:51 +01:00
|
|
|
) -> CodeOffset {
|
2019-11-12 21:08:08 +01:00
|
|
|
let tcx = self.debug_context.tcx;
|
|
|
|
|
|
|
|
let line_program = &mut self.debug_context.dwarf.unit.line_program;
|
|
|
|
|
|
|
|
line_program.begin_sequence(Some(Address::Symbol {
|
|
|
|
symbol: self.symbol,
|
|
|
|
addend: 0,
|
|
|
|
}));
|
|
|
|
|
|
|
|
let encinfo = isa.encoding_info();
|
|
|
|
let func = &context.func;
|
2020-02-14 18:23:29 +01:00
|
|
|
let mut blocks = func.layout.blocks().collect::<Vec<_>>();
|
|
|
|
blocks.sort_by_key(|block| func.offsets[*block]); // Ensure inst offsets always increase
|
2019-11-12 21:08:08 +01:00
|
|
|
|
|
|
|
let line_strings = &mut self.debug_context.dwarf.line_strings;
|
2020-03-14 14:48:04 +01:00
|
|
|
let function_span = self.mir.span;
|
2020-03-14 15:18:04 +01:00
|
|
|
let mut last_span = None;
|
2020-01-14 16:11:23 +01:00
|
|
|
let mut last_file = None;
|
2019-11-12 21:08:08 +01:00
|
|
|
let mut create_row_for_span = |line_program: &mut LineProgram, span: Span| {
|
2020-03-14 15:18:04 +01:00
|
|
|
if let Some(last_span) = last_span {
|
|
|
|
if span == last_span {
|
|
|
|
line_program.generate_row();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
last_span = Some(span);
|
|
|
|
|
2020-03-14 14:48:04 +01:00
|
|
|
// Based on https://github.com/rust-lang/rust/blob/e369d87b015a84653343032833d65d0545fd3f26/src/librustc_codegen_ssa/mir/mod.rs#L116-L131
|
|
|
|
// In order to have a good line stepping behavior in debugger, we overwrite debug
|
|
|
|
// locations of macro expansions with that of the outermost expansion site
|
|
|
|
// (unless the crate is being compiled with `-Z debug-macros`).
|
|
|
|
let span = if !span.from_expansion() ||
|
|
|
|
tcx.sess.opts.debugging_opts.debug_macros {
|
|
|
|
span
|
|
|
|
} else {
|
|
|
|
// Walk up the macro expansion chain until we reach a non-expanded span.
|
|
|
|
// We also stop at the function body level because no line stepping can occur
|
|
|
|
// at the level above that.
|
|
|
|
rustc_span::hygiene::walk_chain(span, function_span.ctxt())
|
|
|
|
};
|
|
|
|
|
2020-03-14 15:10:22 +01:00
|
|
|
let (file, line, col) = match tcx.sess.source_map().lookup_line(span.lo()) {
|
|
|
|
Ok(SourceFileAndLine { sf: file, line }) => {
|
|
|
|
let line_pos = file.line_begin_pos(span.lo());
|
|
|
|
|
|
|
|
(file, u64::try_from(line).unwrap() + 1, u64::from((span.lo() - line_pos).to_u32()) + 1)
|
|
|
|
}
|
|
|
|
Err(file) => (file, 0, 0)
|
|
|
|
};
|
2019-11-12 21:08:08 +01:00
|
|
|
|
2020-01-14 16:11:23 +01:00
|
|
|
// line_program_add_file is very slow.
|
|
|
|
// Optimize for the common case of the current file not being changed.
|
2020-03-14 15:18:04 +01:00
|
|
|
let current_file_changed = if let Some(last_file) = &last_file {
|
2020-01-14 16:11:23 +01:00
|
|
|
// If the allocations are not equal, then the files may still be equal, but that
|
|
|
|
// is not a problem, as this is just an optimization.
|
2020-03-27 12:14:45 +01:00
|
|
|
!rustc_data_structures::sync::Lrc::ptr_eq(last_file, &file)
|
2020-01-14 16:11:23 +01:00
|
|
|
} else {
|
|
|
|
true
|
|
|
|
};
|
|
|
|
if current_file_changed {
|
2020-04-18 14:56:04 +03:00
|
|
|
let file_id = line_program_add_file(line_program, line_strings, &file);
|
2020-01-14 16:11:23 +01:00
|
|
|
line_program.row().file = file_id;
|
2020-03-14 15:10:22 +01:00
|
|
|
last_file = Some(file.clone());
|
2020-01-14 16:11:23 +01:00
|
|
|
}
|
2019-11-12 21:08:08 +01:00
|
|
|
|
2020-03-14 15:10:22 +01:00
|
|
|
line_program.row().line = line;
|
|
|
|
line_program.row().column = col;
|
2019-11-12 21:08:08 +01:00
|
|
|
line_program.generate_row();
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut end = 0;
|
2020-02-14 18:23:29 +01:00
|
|
|
for block in blocks {
|
|
|
|
for (offset, inst, size) in func.inst_offsets(block, &encinfo) {
|
2019-11-12 21:08:08 +01:00
|
|
|
let srcloc = func.srclocs[inst];
|
|
|
|
line_program.row().address_offset = offset as u64;
|
|
|
|
if !srcloc.is_default() {
|
|
|
|
let source_info = *source_info_set.get_index(srcloc.bits() as usize).unwrap();
|
2019-12-24 12:44:07 +01:00
|
|
|
create_row_for_span(line_program, source_info.span);
|
2019-11-12 21:08:08 +01:00
|
|
|
} else {
|
|
|
|
create_row_for_span(line_program, self.mir.span);
|
|
|
|
}
|
|
|
|
end = offset + size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
line_program.end_sequence(end as u64);
|
|
|
|
|
|
|
|
let entry = self.debug_context.dwarf.unit.get_mut(self.entry_id);
|
|
|
|
entry.set(
|
|
|
|
gimli::DW_AT_low_pc,
|
2019-11-12 21:13:15 +01:00
|
|
|
AttributeValue::Address(Address::Symbol {
|
|
|
|
symbol: self.symbol,
|
|
|
|
addend: 0,
|
|
|
|
}),
|
2019-11-12 21:08:08 +01:00
|
|
|
);
|
|
|
|
entry.set(gimli::DW_AT_high_pc, AttributeValue::Udata(end as u64));
|
|
|
|
|
2019-11-12 21:13:15 +01:00
|
|
|
self.debug_context
|
|
|
|
.emit_location(self.entry_id, self.mir.span);
|
2019-11-12 21:08:08 +01:00
|
|
|
|
2019-11-12 21:10:51 +01:00
|
|
|
end
|
2019-11-12 21:08:08 +01:00
|
|
|
}
|
|
|
|
}
|