Skip to content

Various small debuginfo improvements #1470

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Mar 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ pub(crate) fn codegen_fn<'tcx>(
let clif_comments = crate::pretty_clif::CommentWriter::new(tcx, instance);

let func_debug_cx = if let Some(debug_context) = &mut cx.debug_context {
Some(debug_context.define_function(tcx, &symbol_name, mir.span))
Some(debug_context.define_function(tcx, instance, &symbol_name, mir.span))
} else {
None
};
Expand Down
107 changes: 96 additions & 11 deletions src/debuginfo/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ mod unwind;
use cranelift_codegen::ir::Endianness;
use cranelift_codegen::isa::TargetIsa;
use gimli::write::{
Address, AttributeValue, DwarfUnit, FileId, LineProgram, LineString, Range, RangeList,
UnitEntryId,
Address, AttributeValue, DwarfUnit, Expression, FileId, LineProgram, LineString, Range,
RangeList, UnitEntryId,
};
use gimli::{Encoding, Format, LineEncoding, RunTimeEndian};
use gimli::{AArch64, Encoding, Format, LineEncoding, Register, RiscV, RunTimeEndian, X86_64};
use indexmap::IndexSet;
use rustc_codegen_ssa::debuginfo::type_names;
use rustc_hir::def_id::DefIdMap;
use rustc_session::Session;

pub(crate) use self::emit::{DebugReloc, DebugRelocName};
Expand All @@ -28,6 +30,8 @@ pub(crate) struct DebugContext {

dwarf: DwarfUnit,
unit_range_list: RangeList,
stack_pointer_register: Register,
namespace_map: DefIdMap<UnitEntryId>,

should_remap_filepaths: bool,
}
Expand All @@ -39,7 +43,7 @@ pub(crate) struct FunctionDebugContext {
}

impl DebugContext {
pub(crate) fn new(tcx: TyCtxt<'_>, isa: &dyn TargetIsa) -> Self {
pub(crate) fn new(tcx: TyCtxt<'_>, isa: &dyn TargetIsa, cgu_name: &str) -> Self {
let encoding = Encoding {
format: Format::Dwarf32,
// FIXME this should be configurable
Expand All @@ -60,6 +64,15 @@ impl DebugContext {
Endianness::Big => RunTimeEndian::Big,
};

let stack_pointer_register = match isa.triple().architecture {
target_lexicon::Architecture::Aarch64(_) => AArch64::SP,
target_lexicon::Architecture::Riscv64(_) => RiscV::SP,
target_lexicon::Architecture::X86_64 | target_lexicon::Architecture::X86_64h => {
X86_64::RSP
}
_ => Register(u16::MAX),
};

let mut dwarf = DwarfUnit::new(encoding);

let should_remap_filepaths = tcx.sess.should_prefer_remapped_for_codegen();
Expand Down Expand Up @@ -95,14 +108,20 @@ impl DebugContext {
dwarf.unit.line_program = line_program;

{
let name = dwarf.strings.add(name);
let name = dwarf.strings.add(format!("{name}/@/{cgu_name}"));
let comp_dir = dwarf.strings.add(comp_dir);

let root = dwarf.unit.root();
let root = dwarf.unit.get_mut(root);
root.set(gimli::DW_AT_producer, AttributeValue::StringRef(dwarf.strings.add(producer)));
root.set(gimli::DW_AT_language, AttributeValue::Language(gimli::DW_LANG_Rust));
root.set(gimli::DW_AT_name, AttributeValue::StringRef(name));

// This will be replaced when emitting the debuginfo. It is only
// defined here to ensure that the order of the attributes matches
// rustc.
root.set(gimli::DW_AT_stmt_list, AttributeValue::Udata(0));

root.set(gimli::DW_AT_comp_dir, AttributeValue::StringRef(comp_dir));
root.set(gimli::DW_AT_low_pc, AttributeValue::Address(Address::Constant(0)));
}
Expand All @@ -111,33 +130,99 @@ impl DebugContext {
endian,
dwarf,
unit_range_list: RangeList(Vec::new()),
stack_pointer_register,
namespace_map: DefIdMap::default(),
should_remap_filepaths,
}
}

pub(crate) fn define_function(
fn item_namespace(&mut self, tcx: TyCtxt<'_>, def_id: DefId) -> UnitEntryId {
if let Some(&scope) = self.namespace_map.get(&def_id) {
return scope;
}

let def_key = tcx.def_key(def_id);
let parent_scope = def_key
.parent
.map(|parent| self.item_namespace(tcx, DefId { krate: def_id.krate, index: parent }))
.unwrap_or(self.dwarf.unit.root());

let namespace_name = {
let mut output = String::new();
type_names::push_item_name(tcx, def_id, false, &mut output);
output
};
let namespace_name_id = self.dwarf.strings.add(namespace_name);

let scope = self.dwarf.unit.add(parent_scope, gimli::DW_TAG_namespace);
let scope_entry = self.dwarf.unit.get_mut(scope);
scope_entry.set(gimli::DW_AT_name, AttributeValue::StringRef(namespace_name_id));

self.namespace_map.insert(def_id, scope);
scope
}

pub(crate) fn define_function<'tcx>(
&mut self,
tcx: TyCtxt<'_>,
name: &str,
tcx: TyCtxt<'tcx>,
instance: Instance<'tcx>,
linkage_name: &str,
function_span: Span,
) -> FunctionDebugContext {
let (file, line, column) = DebugContext::get_span_loc(tcx, function_span, function_span);

let file_id = self.add_source_file(&file);

// FIXME: add to appropriate scope instead of root
let scope = self.dwarf.unit.root();
let scope = self.item_namespace(tcx, tcx.parent(instance.def_id()));

let mut name = String::new();
type_names::push_item_name(tcx, instance.def_id(), false, &mut name);

// Find the enclosing function, in case this is a closure.
let enclosing_fn_def_id = tcx.typeck_root_def_id(instance.def_id());

// We look up the generics of the enclosing function and truncate the args
// to their length in order to cut off extra stuff that might be in there for
// closures or coroutines.
let generics = tcx.generics_of(enclosing_fn_def_id);
let args = instance.args.truncate_to(tcx, generics);

type_names::push_generic_params(
tcx,
tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), args),
enclosing_fn_def_id,
&mut name,
);

let entry_id = self.dwarf.unit.add(scope, gimli::DW_TAG_subprogram);
let entry = self.dwarf.unit.get_mut(entry_id);
let linkage_name_id =
if name != linkage_name { Some(self.dwarf.strings.add(linkage_name)) } else { None };
let name_id = self.dwarf.strings.add(name);

// These will be replaced in FunctionDebugContext::finalize. They are
// only defined here to ensure that the order of the attributes matches
// rustc.
entry.set(gimli::DW_AT_low_pc, AttributeValue::Udata(0));
entry.set(gimli::DW_AT_high_pc, AttributeValue::Udata(0));

let mut frame_base_expr = Expression::new();
frame_base_expr.op_reg(self.stack_pointer_register);
entry.set(gimli::DW_AT_frame_base, AttributeValue::Exprloc(frame_base_expr));

if let Some(linkage_name_id) = linkage_name_id {
entry.set(gimli::DW_AT_linkage_name, AttributeValue::StringRef(linkage_name_id));
}
// Gdb requires DW_AT_name. Otherwise the DW_TAG_subprogram is skipped.
entry.set(gimli::DW_AT_name, AttributeValue::StringRef(name_id));
entry.set(gimli::DW_AT_linkage_name, AttributeValue::StringRef(name_id));

entry.set(gimli::DW_AT_decl_file, AttributeValue::FileIndex(Some(file_id)));
entry.set(gimli::DW_AT_decl_line, AttributeValue::Udata(line));
entry.set(gimli::DW_AT_decl_column, AttributeValue::Udata(column));

if tcx.is_reachable_non_generic(instance.def_id()) {
entry.set(gimli::DW_AT_external, AttributeValue::FlagPresent);
}

FunctionDebugContext {
entry_id,
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ impl CodegenCx {
let unwind_context =
UnwindContext::new(isa, matches!(backend_config.codegen_mode, CodegenMode::Aot));
let debug_context = if debug_info && !tcx.sess.target.options.is_like_windows {
Some(DebugContext::new(tcx, isa))
Some(DebugContext::new(tcx, isa, cgu_name.as_str()))
} else {
None
};
Expand Down