]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs
Auto merge of #78953 - mzohreva:mz/from_raw_fd, r=Mark-Simulacrum
[rust.git] / compiler / rustc_codegen_cranelift / src / debuginfo / line_info.rs
1 //! Line info generation (`.debug_line`)
2
3 use std::ffi::OsStr;
4 use std::path::{Component, Path};
5
6 use crate::prelude::*;
7
8 use rustc_span::{
9     FileName, Pos, SourceFile, SourceFileAndLine, SourceFileHash, SourceFileHashAlgorithm,
10 };
11
12 use cranelift_codegen::binemit::CodeOffset;
13 use cranelift_codegen::machinst::MachSrcLoc;
14
15 use gimli::write::{
16     Address, AttributeValue, FileId, FileInfo, LineProgram, LineString, LineStringTable,
17     UnitEntryId,
18 };
19
20 // OPTIMIZATION: It is cheaper to do this in one pass than using `.parent()` and `.file_name()`.
21 fn split_path_dir_and_file(path: &Path) -> (&Path, &OsStr) {
22     let mut iter = path.components();
23     let file_name = match iter.next_back() {
24         Some(Component::Normal(p)) => p,
25         component => {
26             panic!(
27                 "Path component {:?} of path {} is an invalid filename",
28                 component,
29                 path.display()
30             );
31         }
32     };
33     let parent = iter.as_path();
34     (parent, file_name)
35 }
36
37 // OPTIMIZATION: Avoid UTF-8 validation on UNIX.
38 fn osstr_as_utf8_bytes(path: &OsStr) -> &[u8] {
39     #[cfg(unix)]
40     {
41         use std::os::unix::ffi::OsStrExt;
42         return path.as_bytes();
43     }
44     #[cfg(not(unix))]
45     {
46         return path.to_str().unwrap().as_bytes();
47     }
48 }
49
50 pub(crate) const MD5_LEN: usize = 16;
51
52 pub(crate) fn make_file_info(hash: SourceFileHash) -> Option<FileInfo> {
53     if hash.kind == SourceFileHashAlgorithm::Md5 {
54         let mut buf = [0u8; MD5_LEN];
55         buf.copy_from_slice(hash.hash_bytes());
56         Some(FileInfo {
57             timestamp: 0,
58             size: 0,
59             md5: buf,
60         })
61     } else {
62         None
63     }
64 }
65
66 fn line_program_add_file(
67     line_program: &mut LineProgram,
68     line_strings: &mut LineStringTable,
69     file: &SourceFile,
70 ) -> FileId {
71     match &file.name {
72         FileName::Real(path) => {
73             let (dir_path, file_name) = split_path_dir_and_file(path.stable_name());
74             let dir_name = osstr_as_utf8_bytes(dir_path.as_os_str());
75             let file_name = osstr_as_utf8_bytes(file_name);
76
77             let dir_id = if !dir_name.is_empty() {
78                 let dir_name = LineString::new(dir_name, line_program.encoding(), line_strings);
79                 line_program.add_directory(dir_name)
80             } else {
81                 line_program.default_directory()
82             };
83             let file_name = LineString::new(file_name, line_program.encoding(), line_strings);
84
85             let info = make_file_info(file.src_hash);
86
87             line_program.file_has_md5 &= info.is_some();
88             line_program.add_file(file_name, dir_id, info)
89         }
90         // FIXME give more appropriate file names
91         filename => {
92             let dir_id = line_program.default_directory();
93             let dummy_file_name = LineString::new(
94                 filename.to_string().into_bytes(),
95                 line_program.encoding(),
96                 line_strings,
97             );
98             line_program.add_file(dummy_file_name, dir_id, None)
99         }
100     }
101 }
102
103 impl<'tcx> DebugContext<'tcx> {
104     pub(super) fn emit_location(&mut self, entry_id: UnitEntryId, span: Span) {
105         let loc = self.tcx.sess.source_map().lookup_char_pos(span.lo());
106
107         let file_id = line_program_add_file(
108             &mut self.dwarf.unit.line_program,
109             &mut self.dwarf.line_strings,
110             &loc.file,
111         );
112
113         let entry = self.dwarf.unit.get_mut(entry_id);
114
115         entry.set(
116             gimli::DW_AT_decl_file,
117             AttributeValue::FileIndex(Some(file_id)),
118         );
119         entry.set(
120             gimli::DW_AT_decl_line,
121             AttributeValue::Udata(loc.line as u64),
122         );
123         // FIXME: probably omit this
124         entry.set(
125             gimli::DW_AT_decl_column,
126             AttributeValue::Udata(loc.col.to_usize() as u64),
127         );
128     }
129
130     pub(super) fn create_debug_lines(
131         &mut self,
132         isa: &dyn cranelift_codegen::isa::TargetIsa,
133         symbol: usize,
134         entry_id: UnitEntryId,
135         context: &Context,
136         function_span: Span,
137         source_info_set: &indexmap::IndexSet<SourceInfo>,
138     ) -> CodeOffset {
139         let tcx = self.tcx;
140         let line_program = &mut self.dwarf.unit.line_program;
141         let func = &context.func;
142
143         let line_strings = &mut self.dwarf.line_strings;
144         let mut last_span = None;
145         let mut last_file = None;
146         let mut create_row_for_span = |line_program: &mut LineProgram, span: Span| {
147             if let Some(last_span) = last_span {
148                 if span == last_span {
149                     line_program.generate_row();
150                     return;
151                 }
152             }
153             last_span = Some(span);
154
155             // Based on https://github.com/rust-lang/rust/blob/e369d87b015a84653343032833d65d0545fd3f26/src/librustc_codegen_ssa/mir/mod.rs#L116-L131
156             // In order to have a good line stepping behavior in debugger, we overwrite debug
157             // locations of macro expansions with that of the outermost expansion site
158             // (unless the crate is being compiled with `-Z debug-macros`).
159             let span = if !span.from_expansion() || tcx.sess.opts.debugging_opts.debug_macros {
160                 span
161             } else {
162                 // Walk up the macro expansion chain until we reach a non-expanded span.
163                 // We also stop at the function body level because no line stepping can occur
164                 // at the level above that.
165                 rustc_span::hygiene::walk_chain(span, function_span.ctxt())
166             };
167
168             let (file, line, col) = match tcx.sess.source_map().lookup_line(span.lo()) {
169                 Ok(SourceFileAndLine { sf: file, line }) => {
170                     let line_pos = file.line_begin_pos(span.lo());
171
172                     (
173                         file,
174                         u64::try_from(line).unwrap() + 1,
175                         u64::from((span.lo() - line_pos).to_u32()) + 1,
176                     )
177                 }
178                 Err(file) => (file, 0, 0),
179             };
180
181             // line_program_add_file is very slow.
182             // Optimize for the common case of the current file not being changed.
183             let current_file_changed = if let Some(last_file) = &last_file {
184                 // If the allocations are not equal, then the files may still be equal, but that
185                 // is not a problem, as this is just an optimization.
186                 !rustc_data_structures::sync::Lrc::ptr_eq(last_file, &file)
187             } else {
188                 true
189             };
190             if current_file_changed {
191                 let file_id = line_program_add_file(line_program, line_strings, &file);
192                 line_program.row().file = file_id;
193                 last_file = Some(file);
194             }
195
196             line_program.row().line = line;
197             line_program.row().column = col;
198             line_program.generate_row();
199         };
200
201         line_program.begin_sequence(Some(Address::Symbol { symbol, addend: 0 }));
202
203         let mut func_end = 0;
204
205         if let Some(ref mcr) = &context.mach_compile_result {
206             for &MachSrcLoc { start, end, loc } in mcr.buffer.get_srclocs_sorted() {
207                 line_program.row().address_offset = u64::from(start);
208                 if !loc.is_default() {
209                     let source_info = *source_info_set.get_index(loc.bits() as usize).unwrap();
210                     create_row_for_span(line_program, source_info.span);
211                 } else {
212                     create_row_for_span(line_program, function_span);
213                 }
214                 func_end = end;
215             }
216
217             line_program.end_sequence(u64::from(func_end));
218
219             func_end = mcr.buffer.total_size();
220         } else {
221             let encinfo = isa.encoding_info();
222             let mut blocks = func.layout.blocks().collect::<Vec<_>>();
223             blocks.sort_by_key(|block| func.offsets[*block]); // Ensure inst offsets always increase
224
225             for block in blocks {
226                 for (offset, inst, size) in func.inst_offsets(block, &encinfo) {
227                     let srcloc = func.srclocs[inst];
228                     line_program.row().address_offset = u64::from(offset);
229                     if !srcloc.is_default() {
230                         let source_info =
231                             *source_info_set.get_index(srcloc.bits() as usize).unwrap();
232                         create_row_for_span(line_program, source_info.span);
233                     } else {
234                         create_row_for_span(line_program, function_span);
235                     }
236                     func_end = offset + size;
237                 }
238             }
239             line_program.end_sequence(u64::from(func_end));
240         }
241
242         assert_ne!(func_end, 0);
243
244         let entry = self.dwarf.unit.get_mut(entry_id);
245         entry.set(
246             gimli::DW_AT_low_pc,
247             AttributeValue::Address(Address::Symbol { symbol, addend: 0 }),
248         );
249         entry.set(
250             gimli::DW_AT_high_pc,
251             AttributeValue::Udata(u64::from(func_end)),
252         );
253
254         self.emit_location(entry_id, function_span);
255
256         func_end
257     }
258 }