/rust/registry/src/index.crates.io-1949cf8c6b5b557f/addr2line-0.24.2/src/line.rs
Line | Count | Source |
1 | | use alloc::boxed::Box; |
2 | | use alloc::string::{String, ToString}; |
3 | | use alloc::vec::Vec; |
4 | | use core::cmp::Ordering; |
5 | | use core::mem; |
6 | | use core::num::NonZeroU64; |
7 | | |
8 | | use crate::lazy::LazyResult; |
9 | | use crate::{Error, Location}; |
10 | | |
11 | | pub(crate) struct LazyLines(LazyResult<Lines>); |
12 | | |
13 | | impl LazyLines { |
14 | 0 | pub(crate) fn new() -> Self { |
15 | 0 | LazyLines(LazyResult::new()) |
16 | 0 | } |
17 | | |
18 | 0 | pub(crate) fn borrow<R: gimli::Reader>( |
19 | 0 | &self, |
20 | 0 | dw_unit: gimli::UnitRef<R>, |
21 | 0 | ilnp: &gimli::IncompleteLineProgram<R, R::Offset>, |
22 | 0 | ) -> Result<&Lines, Error> { |
23 | 0 | self.0 |
24 | 0 | .borrow_with(|| Lines::parse(dw_unit, ilnp.clone())) Unexecuted instantiation: <addr2line::line::LazyLines>::borrow::<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>::{closure#0}Unexecuted instantiation: <addr2line::line::LazyLines>::borrow::<_>::{closure#0} |
25 | 0 | .as_ref() |
26 | 0 | .map_err(Error::clone) |
27 | 0 | } Unexecuted instantiation: <addr2line::line::LazyLines>::borrow::<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>> Unexecuted instantiation: <addr2line::line::LazyLines>::borrow::<_> |
28 | | } |
29 | | |
30 | | struct LineSequence { |
31 | | start: u64, |
32 | | end: u64, |
33 | | rows: Box<[LineRow]>, |
34 | | } |
35 | | |
36 | | struct LineRow { |
37 | | address: u64, |
38 | | file_index: u64, |
39 | | line: u32, |
40 | | column: u32, |
41 | | } |
42 | | |
43 | | pub(crate) struct Lines { |
44 | | files: Box<[String]>, |
45 | | sequences: Box<[LineSequence]>, |
46 | | } |
47 | | |
48 | | impl Lines { |
49 | 0 | fn parse<R: gimli::Reader>( |
50 | 0 | dw_unit: gimli::UnitRef<R>, |
51 | 0 | ilnp: gimli::IncompleteLineProgram<R, R::Offset>, |
52 | 0 | ) -> Result<Self, Error> { |
53 | 0 | let mut sequences = Vec::new(); |
54 | 0 | let mut sequence_rows = Vec::<LineRow>::new(); |
55 | 0 | let mut rows = ilnp.rows(); |
56 | 0 | while let Some((_, row)) = rows.next_row()? { |
57 | 0 | if row.end_sequence() { |
58 | 0 | if let Some(start) = sequence_rows.first().map(|x| x.address) { |
59 | 0 | let end = row.address(); |
60 | 0 | let mut rows = Vec::new(); |
61 | 0 | mem::swap(&mut rows, &mut sequence_rows); |
62 | 0 | sequences.push(LineSequence { |
63 | 0 | start, |
64 | 0 | end, |
65 | 0 | rows: rows.into_boxed_slice(), |
66 | 0 | }); |
67 | 0 | } |
68 | 0 | continue; |
69 | 0 | } |
70 | | |
71 | 0 | let address = row.address(); |
72 | 0 | let file_index = row.file_index(); |
73 | | // Convert line and column to u32 to save a little memory. |
74 | | // We'll handle the special case of line 0 later, |
75 | | // and return left edge as column 0 in the public API. |
76 | 0 | let line = row.line().map(NonZeroU64::get).unwrap_or(0) as u32; |
77 | 0 | let column = match row.column() { |
78 | 0 | gimli::ColumnType::LeftEdge => 0, |
79 | 0 | gimli::ColumnType::Column(x) => x.get() as u32, |
80 | | }; |
81 | | |
82 | 0 | if let Some(last_row) = sequence_rows.last_mut() { |
83 | 0 | if last_row.address == address { |
84 | 0 | last_row.file_index = file_index; |
85 | 0 | last_row.line = line; |
86 | 0 | last_row.column = column; |
87 | 0 | continue; |
88 | 0 | } |
89 | 0 | } |
90 | | |
91 | 0 | sequence_rows.push(LineRow { |
92 | 0 | address, |
93 | 0 | file_index, |
94 | 0 | line, |
95 | 0 | column, |
96 | 0 | }); |
97 | | } |
98 | 0 | sequences.sort_by_key(|x| x.start); |
99 | | |
100 | 0 | let mut files = Vec::new(); |
101 | 0 | let header = rows.header(); |
102 | 0 | match header.file(0) { |
103 | 0 | Some(file) => files.push(render_file(dw_unit, file, header)?), |
104 | 0 | None => files.push(String::from("")), // DWARF version <= 4 may not have 0th index |
105 | | } |
106 | 0 | let mut index = 1; |
107 | 0 | while let Some(file) = header.file(index) { |
108 | 0 | files.push(render_file(dw_unit, file, header)?); |
109 | 0 | index += 1; |
110 | | } |
111 | | |
112 | 0 | Ok(Self { |
113 | 0 | files: files.into_boxed_slice(), |
114 | 0 | sequences: sequences.into_boxed_slice(), |
115 | 0 | }) |
116 | 0 | } Unexecuted instantiation: <addr2line::line::Lines>::parse::<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>> Unexecuted instantiation: <addr2line::line::Lines>::parse::<_> |
117 | | |
118 | 0 | pub(crate) fn file(&self, index: u64) -> Option<&str> { |
119 | 0 | self.files.get(index as usize).map(String::as_str) |
120 | 0 | } |
121 | | |
122 | 0 | pub(crate) fn ranges(&self) -> impl Iterator<Item = gimli::Range> + '_ { |
123 | 0 | self.sequences.iter().map(|sequence| gimli::Range { |
124 | 0 | begin: sequence.start, |
125 | 0 | end: sequence.end, |
126 | 0 | }) Unexecuted instantiation: <addr2line::line::Lines>::ranges::{closure#0}Unexecuted instantiation: <addr2line::line::Lines>::ranges::{closure#0} |
127 | 0 | } |
128 | | |
129 | 0 | fn row_location(&self, row: &LineRow) -> Location<'_> { |
130 | 0 | let file = self.files.get(row.file_index as usize).map(String::as_str); |
131 | | Location { |
132 | 0 | file, |
133 | 0 | line: if row.line != 0 { Some(row.line) } else { None }, |
134 | | // If row.line is specified then row.column always has meaning. |
135 | 0 | column: if row.line != 0 { |
136 | 0 | Some(row.column) |
137 | | } else { |
138 | 0 | None |
139 | | }, |
140 | | } |
141 | 0 | } |
142 | | |
143 | 0 | pub(crate) fn find_location(&self, probe: u64) -> Result<Option<Location<'_>>, Error> { |
144 | 0 | let seq_idx = self.sequences.binary_search_by(|sequence| { |
145 | 0 | if probe < sequence.start { |
146 | 0 | Ordering::Greater |
147 | 0 | } else if probe >= sequence.end { |
148 | 0 | Ordering::Less |
149 | | } else { |
150 | 0 | Ordering::Equal |
151 | | } |
152 | 0 | }); |
153 | 0 | let seq_idx = match seq_idx { |
154 | 0 | Ok(x) => x, |
155 | 0 | Err(_) => return Ok(None), |
156 | | }; |
157 | 0 | let sequence = &self.sequences[seq_idx]; |
158 | | |
159 | 0 | let idx = sequence |
160 | 0 | .rows |
161 | 0 | .binary_search_by(|row| row.address.cmp(&probe)); |
162 | 0 | let idx = match idx { |
163 | 0 | Ok(x) => x, |
164 | 0 | Err(0) => return Ok(None), |
165 | 0 | Err(x) => x - 1, |
166 | | }; |
167 | 0 | Ok(Some(self.row_location(&sequence.rows[idx]))) |
168 | 0 | } |
169 | | |
170 | 0 | pub(crate) fn find_location_range( |
171 | 0 | &self, |
172 | 0 | probe_low: u64, |
173 | 0 | probe_high: u64, |
174 | 0 | ) -> Result<LineLocationRangeIter<'_>, Error> { |
175 | | // Find index for probe_low. |
176 | 0 | let seq_idx = self.sequences.binary_search_by(|sequence| { |
177 | 0 | if probe_low < sequence.start { |
178 | 0 | Ordering::Greater |
179 | 0 | } else if probe_low >= sequence.end { |
180 | 0 | Ordering::Less |
181 | | } else { |
182 | 0 | Ordering::Equal |
183 | | } |
184 | 0 | }); |
185 | 0 | let seq_idx = match seq_idx { |
186 | 0 | Ok(x) => x, |
187 | 0 | Err(x) => x, // probe below sequence, but range could overlap |
188 | | }; |
189 | | |
190 | 0 | let row_idx = if let Some(seq) = self.sequences.get(seq_idx) { |
191 | 0 | let idx = seq.rows.binary_search_by(|row| row.address.cmp(&probe_low)); |
192 | 0 | match idx { |
193 | 0 | Ok(x) => x, |
194 | 0 | Err(0) => 0, // probe below sequence, but range could overlap |
195 | 0 | Err(x) => x - 1, |
196 | | } |
197 | | } else { |
198 | 0 | 0 |
199 | | }; |
200 | | |
201 | 0 | Ok(LineLocationRangeIter { |
202 | 0 | lines: self, |
203 | 0 | seq_idx, |
204 | 0 | row_idx, |
205 | 0 | probe_high, |
206 | 0 | }) |
207 | 0 | } |
208 | | } |
209 | | |
210 | | pub(crate) struct LineLocationRangeIter<'ctx> { |
211 | | lines: &'ctx Lines, |
212 | | seq_idx: usize, |
213 | | row_idx: usize, |
214 | | probe_high: u64, |
215 | | } |
216 | | |
217 | | impl<'ctx> Iterator for LineLocationRangeIter<'ctx> { |
218 | | type Item = (u64, u64, Location<'ctx>); |
219 | | |
220 | 0 | fn next(&mut self) -> Option<(u64, u64, Location<'ctx>)> { |
221 | 0 | while let Some(seq) = self.lines.sequences.get(self.seq_idx) { |
222 | 0 | if seq.start >= self.probe_high { |
223 | 0 | break; |
224 | 0 | } |
225 | | |
226 | 0 | match seq.rows.get(self.row_idx) { |
227 | 0 | Some(row) => { |
228 | 0 | if row.address >= self.probe_high { |
229 | 0 | break; |
230 | 0 | } |
231 | | |
232 | 0 | let nextaddr = seq |
233 | 0 | .rows |
234 | 0 | .get(self.row_idx + 1) |
235 | 0 | .map(|row| row.address) |
236 | 0 | .unwrap_or(seq.end); |
237 | | |
238 | 0 | let item = ( |
239 | 0 | row.address, |
240 | 0 | nextaddr - row.address, |
241 | 0 | self.lines.row_location(row), |
242 | 0 | ); |
243 | 0 | self.row_idx += 1; |
244 | | |
245 | 0 | return Some(item); |
246 | | } |
247 | 0 | None => { |
248 | 0 | self.seq_idx += 1; |
249 | 0 | self.row_idx = 0; |
250 | 0 | } |
251 | | } |
252 | | } |
253 | 0 | None |
254 | 0 | } |
255 | | } |
256 | | |
257 | 0 | fn render_file<R: gimli::Reader>( |
258 | 0 | dw_unit: gimli::UnitRef<R>, |
259 | 0 | file: &gimli::FileEntry<R, R::Offset>, |
260 | 0 | header: &gimli::LineProgramHeader<R, R::Offset>, |
261 | 0 | ) -> Result<String, gimli::Error> { |
262 | 0 | let mut path = if let Some(ref comp_dir) = dw_unit.comp_dir { |
263 | 0 | comp_dir.to_string_lossy()?.into_owned() |
264 | | } else { |
265 | 0 | String::new() |
266 | | }; |
267 | | |
268 | | // The directory index 0 is defined to correspond to the compilation unit directory. |
269 | 0 | if file.directory_index() != 0 { |
270 | 0 | if let Some(directory) = file.directory(header) { |
271 | 0 | path_push( |
272 | 0 | &mut path, |
273 | 0 | dw_unit.attr_string(directory)?.to_string_lossy()?.as_ref(), |
274 | | ); |
275 | 0 | } |
276 | 0 | } |
277 | | |
278 | 0 | path_push( |
279 | 0 | &mut path, |
280 | 0 | dw_unit |
281 | 0 | .attr_string(file.path_name())? |
282 | 0 | .to_string_lossy()? |
283 | 0 | .as_ref(), |
284 | | ); |
285 | | |
286 | 0 | Ok(path) |
287 | 0 | } Unexecuted instantiation: addr2line::line::render_file::<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>> Unexecuted instantiation: addr2line::line::render_file::<_> |
288 | | |
289 | 0 | fn path_push(path: &mut String, p: &str) { |
290 | 0 | if has_unix_root(p) || has_windows_root(p) { |
291 | 0 | *path = p.to_string(); |
292 | 0 | } else { |
293 | 0 | let dir_separator = if has_windows_root(path.as_str()) { |
294 | 0 | '\\' |
295 | | } else { |
296 | 0 | '/' |
297 | | }; |
298 | | |
299 | 0 | if !path.is_empty() && !path.ends_with(dir_separator) { |
300 | 0 | path.push(dir_separator); |
301 | 0 | } |
302 | 0 | *path += p; |
303 | | } |
304 | 0 | } |
305 | | |
306 | | /// Check if the path in the given string has a unix style root |
307 | 0 | fn has_unix_root(p: &str) -> bool { |
308 | 0 | p.starts_with('/') |
309 | 0 | } |
310 | | |
311 | | /// Check if the path in the given string has a windows style root |
312 | 0 | fn has_windows_root(p: &str) -> bool { |
313 | 0 | p.starts_with('\\') || p.get(1..3) == Some(":\\") |
314 | 0 | } |