1 //! Line info generation (`.debug_line`)
2
3 use std::ffi::OsStr;
4 use std::path::{Component, Path};
5
6 use crate::debuginfo::FunctionDebugContext;
7 use crate::prelude::*;
8
9 use rustc_data_structures::sync::Lrc;
10 use rustc_span::{
11 FileName, Pos, SourceFile, SourceFileAndLine, SourceFileHash, SourceFileHashAlgorithm,
12 };
13
14 use cranelift_codegen::binemit::CodeOffset;
15 use cranelift_codegen::MachSrcLoc;
16
17 use gimli::write::{
18 Address, AttributeValue, FileId, FileInfo, LineProgram, LineString, LineStringTable,
19 };
20
21 // OPTIMIZATION: It is cheaper to do this in one pass than using `.parent()` and `.file_name()`.
split_path_dir_and_file(path: &Path) -> (&Path, &OsStr)22 fn split_path_dir_and_file(path: &Path) -> (&Path, &OsStr) {
23 let mut iter = path.components();
24 let file_name = match iter.next_back() {
25 Some(Component::Normal(p)) => p,
26 component => {
27 panic!(
28 "Path component {:?} of path {} is an invalid filename",
29 component,
30 path.display()
31 );
32 }
33 };
34 let parent = iter.as_path();
35 (parent, file_name)
36 }
37
38 // OPTIMIZATION: Avoid UTF-8 validation on UNIX.
osstr_as_utf8_bytes(path: &OsStr) -> &[u8]39 fn osstr_as_utf8_bytes(path: &OsStr) -> &[u8] {
40 #[cfg(unix)]
41 {
42 use std::os::unix::ffi::OsStrExt;
43 path.as_bytes()
44 }
45 #[cfg(not(unix))]
46 {
47 path.to_str().unwrap().as_bytes()
48 }
49 }
50
51 const MD5_LEN: usize = 16;
52
make_file_info(hash: SourceFileHash) -> Option<FileInfo>53 fn make_file_info(hash: SourceFileHash) -> Option<FileInfo> {
54 if hash.kind == SourceFileHashAlgorithm::Md5 {
55 let mut buf = [0u8; MD5_LEN];
56 buf.copy_from_slice(hash.hash_bytes());
57 Some(FileInfo { timestamp: 0, size: 0, md5: buf })
58 } else {
59 None
60 }
61 }
62
63 impl DebugContext {
get_span_loc( tcx: TyCtxt<'_>, function_span: Span, span: Span, ) -> (Lrc<SourceFile>, u64, u64)64 pub(crate) fn get_span_loc(
65 tcx: TyCtxt<'_>,
66 function_span: Span,
67 span: Span,
68 ) -> (Lrc<SourceFile>, u64, u64) {
69 // Based on https://github.com/rust-lang/rust/blob/e369d87b015a84653343032833d65d0545fd3f26/src/librustc_codegen_ssa/mir/mod.rs#L116-L131
70 // In order to have a good line stepping behavior in debugger, we overwrite debug
71 // locations of macro expansions with that of the outermost expansion site (when the macro is
72 // annotated with `#[collapse_debuginfo]` or when `-Zdebug-macros` is provided).
73 let span = if tcx.should_collapse_debuginfo(span) {
74 span
75 } else {
76 // Walk up the macro expansion chain until we reach a non-expanded span.
77 // We also stop at the function body level because no line stepping can occur
78 // at the level above that.
79 rustc_span::hygiene::walk_chain(span, function_span.ctxt())
80 };
81
82 match tcx.sess.source_map().lookup_line(span.lo()) {
83 Ok(SourceFileAndLine { sf: file, line }) => {
84 let line_pos = file.lines(|lines| lines[line]);
85
86 (
87 file,
88 u64::try_from(line).unwrap() + 1,
89 u64::from((span.lo() - line_pos).to_u32()) + 1,
90 )
91 }
92 Err(file) => (file, 0, 0),
93 }
94 }
95
add_source_file(&mut self, source_file: &SourceFile) -> FileId96 pub(crate) fn add_source_file(&mut self, source_file: &SourceFile) -> FileId {
97 let line_program: &mut LineProgram = &mut self.dwarf.unit.line_program;
98 let line_strings: &mut LineStringTable = &mut self.dwarf.line_strings;
99
100 match &source_file.name {
101 FileName::Real(path) => {
102 let (dir_path, file_name) =
103 split_path_dir_and_file(path.remapped_path_if_available());
104 let dir_name = osstr_as_utf8_bytes(dir_path.as_os_str());
105 let file_name = osstr_as_utf8_bytes(file_name);
106
107 let dir_id = if !dir_name.is_empty() {
108 let dir_name = LineString::new(dir_name, line_program.encoding(), line_strings);
109 line_program.add_directory(dir_name)
110 } else {
111 line_program.default_directory()
112 };
113 let file_name = LineString::new(file_name, line_program.encoding(), line_strings);
114
115 let info = make_file_info(source_file.src_hash);
116
117 line_program.file_has_md5 &= info.is_some();
118 line_program.add_file(file_name, dir_id, info)
119 }
120 // FIXME give more appropriate file names
121 filename => {
122 let dir_id = line_program.default_directory();
123 let dummy_file_name = LineString::new(
124 filename.prefer_remapped().to_string().into_bytes(),
125 line_program.encoding(),
126 line_strings,
127 );
128 line_program.add_file(dummy_file_name, dir_id, None)
129 }
130 }
131 }
132 }
133
134 impl FunctionDebugContext {
add_dbg_loc(&mut self, file_id: FileId, line: u64, column: u64) -> SourceLoc135 pub(crate) fn add_dbg_loc(&mut self, file_id: FileId, line: u64, column: u64) -> SourceLoc {
136 let (index, _) = self.source_loc_set.insert_full((file_id, line, column));
137 SourceLoc::new(u32::try_from(index).unwrap())
138 }
139
create_debug_lines( &mut self, debug_context: &mut DebugContext, symbol: usize, context: &Context, ) -> CodeOffset140 pub(super) fn create_debug_lines(
141 &mut self,
142 debug_context: &mut DebugContext,
143 symbol: usize,
144 context: &Context,
145 ) -> CodeOffset {
146 let create_row_for_span =
147 |debug_context: &mut DebugContext, source_loc: (FileId, u64, u64)| {
148 let (file_id, line, col) = source_loc;
149
150 debug_context.dwarf.unit.line_program.row().file = file_id;
151 debug_context.dwarf.unit.line_program.row().line = line;
152 debug_context.dwarf.unit.line_program.row().column = col;
153 debug_context.dwarf.unit.line_program.generate_row();
154 };
155
156 debug_context
157 .dwarf
158 .unit
159 .line_program
160 .begin_sequence(Some(Address::Symbol { symbol, addend: 0 }));
161
162 let mut func_end = 0;
163
164 let mcr = context.compiled_code().unwrap();
165 for &MachSrcLoc { start, end, loc } in mcr.buffer.get_srclocs_sorted() {
166 debug_context.dwarf.unit.line_program.row().address_offset = u64::from(start);
167 if !loc.is_default() {
168 let source_loc = *self.source_loc_set.get_index(loc.bits() as usize).unwrap();
169 create_row_for_span(debug_context, source_loc);
170 } else {
171 create_row_for_span(debug_context, self.function_source_loc);
172 }
173 func_end = end;
174 }
175
176 debug_context.dwarf.unit.line_program.end_sequence(u64::from(func_end));
177
178 let func_end = mcr.buffer.total_size();
179
180 assert_ne!(func_end, 0);
181
182 let entry = debug_context.dwarf.unit.get_mut(self.entry_id);
183 entry.set(
184 gimli::DW_AT_low_pc,
185 AttributeValue::Address(Address::Symbol { symbol, addend: 0 }),
186 );
187 entry.set(gimli::DW_AT_high_pc, AttributeValue::Udata(u64::from(func_end)));
188
189 func_end
190 }
191 }
192