blazesym/dwarf/
location.rs

1// Based on gimli-rs/addr2line (https://github.com/gimli-rs/addr2line):
2// > Copyright (c) 2016-2018 The gimli Developers
3// >
4// > Permission is hereby granted, free of charge, to any
5// > person obtaining a copy of this software and associated
6// > documentation files (the "Software"), to deal in the
7// > Software without restriction, including without
8// > limitation the rights to use, copy, modify, merge,
9// > publish, distribute, sublicense, and/or sell copies of
10// > the Software, and to permit persons to whom the Software
11// > is furnished to do so, subject to the following
12// > conditions:
13// >
14// > The above copyright notice and this permission notice
15// > shall be included in all copies or substantial portions
16// > of the Software.
17// >
18// > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
19// > ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
20// > TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
21// > PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
22// > SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
23// > CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
24// > OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
25// > IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26// > DEALINGS IN THE SOFTWARE.
27
28use std::cmp::Ordering;
29use std::ffi::OsStr;
30use std::path::Path;
31
32use super::lines::LineSequence;
33use super::lines::Lines;
34use super::unit::Unit;
35use super::units::Units;
36
37
38/// A source location.
39#[derive(Debug, PartialEq)]
40pub struct Location<'dwarf> {
41    /// The directory.
42    pub dir: &'dwarf Path,
43    /// The file name.
44    pub file: &'dwarf OsStr,
45    /// The line number.
46    pub line: Option<u32>,
47    /// The column number.
48    pub column: Option<u32>,
49}
50
51
52pub(super) struct LocationRangeUnitIter<'unit, 'dwarf> {
53    lines: &'unit Lines<'dwarf>,
54    seqs: &'unit [LineSequence],
55    seq_idx: usize,
56    row_idx: usize,
57    probe_high: u64,
58}
59
60impl<'unit, 'dwarf> LocationRangeUnitIter<'unit, 'dwarf> {
61    pub(super) fn new(
62        unit: &'unit Unit<'dwarf>,
63        units: &Units<'dwarf>,
64        probe_low: u64,
65        probe_high: u64,
66    ) -> Result<Option<Self>, gimli::Error> {
67        let unit_ref = units.unit_ref(unit.dw_unit());
68        let lines = unit.parse_lines(unit_ref)?;
69
70        if let Some(lines) = lines {
71            // Find index for probe_low.
72            let seq_idx = lines.sequences.binary_search_by(|sequence| {
73                if probe_low < sequence.start {
74                    Ordering::Greater
75                } else if probe_low >= sequence.end {
76                    Ordering::Less
77                } else {
78                    Ordering::Equal
79                }
80            });
81            let seq_idx = match seq_idx {
82                Ok(x) => x,
83                Err(0) => 0, // probe below sequence, but range could overlap
84                Err(_) => lines.sequences.len(),
85            };
86
87            let row_idx = if let Some(seq) = lines.sequences.get(seq_idx) {
88                let idx = seq.rows.binary_search_by(|row| row.address.cmp(&probe_low));
89                match idx {
90                    Ok(x) => x,
91                    Err(0) => 0, // probe below sequence, but range could overlap
92                    Err(x) => x - 1,
93                }
94            } else {
95                0
96            };
97
98            Ok(Some(Self {
99                lines,
100                seqs: &*lines.sequences,
101                seq_idx,
102                row_idx,
103                probe_high,
104            }))
105        } else {
106            Ok(None)
107        }
108    }
109}
110
111impl<'unit> Iterator for LocationRangeUnitIter<'unit, '_> {
112    type Item = (u64, u64, Location<'unit>);
113
114    fn next(&mut self) -> Option<(u64, u64, Location<'unit>)> {
115        while let Some(seq) = self.seqs.get(self.seq_idx) {
116            if seq.start >= self.probe_high {
117                break
118            }
119
120            match seq.rows.get(self.row_idx) {
121                Some(row) => {
122                    if row.address >= self.probe_high {
123                        break
124                    }
125
126                    // SANITY: We always have a file present for each
127                    //         `file_index`.
128                    let (dir, file) = self.lines.files.get(row.file_index as usize).unwrap();
129                    let nextaddr = seq
130                        .rows
131                        .get(self.row_idx + 1)
132                        .map(|row| row.address)
133                        .unwrap_or(seq.end);
134
135                    let item = (
136                        row.address,
137                        nextaddr - row.address,
138                        Location {
139                            dir,
140                            file,
141                            line: if row.line != 0 { Some(row.line) } else { None },
142                            column: if row.column != 0 {
143                                Some(row.column)
144                            } else {
145                                None
146                            },
147                        },
148                    );
149                    self.row_idx += 1;
150
151                    return Some(item)
152                }
153                None => {
154                    self.seq_idx += 1;
155                    self.row_idx = 0;
156                }
157            }
158        }
159        None
160    }
161}