wasmtime_internal_cranelift/debug/transform/
line_program.rs1use super::address_transform::AddressTransform;
2use super::{Reader, TransformError};
3use anyhow::{Error, bail};
4use gimli::{DebugLineOffset, LineEncoding, Unit, write};
5use wasmtime_environ::DefinedFuncIndex;
6
7#[derive(Debug)]
8enum SavedLineProgramRow {
9 Normal {
10 address: u64,
11 op_index: u64,
12 file_index: u64,
13 line: u64,
14 column: u64,
15 discriminator: u64,
16 is_stmt: bool,
17 basic_block: bool,
18 prologue_end: bool,
19 epilogue_begin: bool,
20 isa: u64,
21 },
22 EndOfSequence,
23}
24
25#[derive(Debug)]
26struct FuncRows {
27 index: DefinedFuncIndex,
28 sorted_rows: Vec<(u64, SavedLineProgramRow)>,
29}
30
31#[derive(Debug, Eq, PartialEq)]
32enum ReadLineProgramState {
33 SequenceEnded,
34 ReadSequence(DefinedFuncIndex),
35 IgnoreSequence,
36}
37
38pub(crate) fn clone_line_program<R>(
39 dwarf: &gimli::Dwarf<R>,
40 unit: &Unit<R, R::Offset>,
41 comp_name: Option<R>,
42 addr_tr: &AddressTransform,
43 out_encoding: gimli::Encoding,
44 out_strings: &mut write::StringTable,
45) -> Result<(write::LineProgram, DebugLineOffset, Vec<write::FileId>, u64), Error>
46where
47 R: Reader,
48{
49 if let Some(program) = unit.line_program.clone() {
50 let header = program.header();
51 let offset = header.offset();
52 let file_index_base = if header.version() < 5 { 1 } else { 0 };
53 assert!(header.version() <= 5, "not supported 6");
54 let line_encoding = LineEncoding {
55 minimum_instruction_length: header.minimum_instruction_length(),
56 maximum_operations_per_instruction: header.maximum_operations_per_instruction(),
57 default_is_stmt: header.default_is_stmt(),
58 line_base: header.line_base(),
59 line_range: header.line_range(),
60 };
61 let out_comp_dir = match header.directory(0) {
62 Some(comp_dir) => clone_line_string(
63 dwarf.attr_string(unit, comp_dir)?,
64 gimli::DW_FORM_string,
65 out_strings,
66 )?,
67 None => write::LineString::String(Vec::new()),
68 };
69 let out_comp_name = match comp_name {
70 Some(comp_name) => clone_line_string(comp_name, gimli::DW_FORM_strp, out_strings)?,
71 None => write::LineString::String(Vec::new()),
72 };
73
74 let mut out_program = write::LineProgram::new(
75 out_encoding,
76 line_encoding,
77 out_comp_dir,
78 None,
79 out_comp_name,
80 None,
81 );
82 let mut dirs = Vec::new();
83 dirs.push(out_program.default_directory());
84 for dir_attr in header.include_directories() {
85 let dir_id = out_program.add_directory(clone_line_string(
86 dwarf.attr_string(unit, dir_attr.clone())?,
87 gimli::DW_FORM_string,
88 out_strings,
89 )?);
90 dirs.push(dir_id);
91 }
92 let mut files = Vec::new();
93 let directory_index_correction = if header.version() >= 5 { 1 } else { 0 };
95 for file_entry in header.file_names() {
96 let dir_index = file_entry.directory_index() + directory_index_correction;
97 let dir_id = dirs[dir_index as usize];
98 let file_id = out_program.add_file(
99 clone_line_string(
100 dwarf.attr_string(unit, file_entry.path_name())?,
101 gimli::DW_FORM_string,
102 out_strings,
103 )?,
104 dir_id,
105 None,
106 );
107 files.push(file_id);
108 }
109
110 let mut rows = program.rows();
111 let mut func_rows = Vec::new();
112 let mut saved_rows: Vec<(u64, SavedLineProgramRow)> = Vec::new();
113 let mut state = ReadLineProgramState::SequenceEnded;
114 while let Some((_header, row)) = rows.next_row()? {
115 if state == ReadLineProgramState::IgnoreSequence {
116 if row.end_sequence() {
117 state = ReadLineProgramState::SequenceEnded;
118 }
119 continue;
120 }
121 let saved_row = if row.end_sequence() {
122 let index = match state {
123 ReadLineProgramState::ReadSequence(index) => index,
124 _ => panic!(),
125 };
126 saved_rows.sort_by_key(|r| r.0);
127 func_rows.push(FuncRows {
128 index,
129 sorted_rows: saved_rows,
130 });
131
132 saved_rows = Vec::new();
133 state = ReadLineProgramState::SequenceEnded;
134 SavedLineProgramRow::EndOfSequence
135 } else {
136 if state == ReadLineProgramState::SequenceEnded {
137 if row.address() == 0 {
139 state = ReadLineProgramState::IgnoreSequence;
140 continue;
141 }
142 match addr_tr.find_func_index(row.address()) {
143 Some(index) => {
144 state = ReadLineProgramState::ReadSequence(index);
145 }
146 None => {
147 state = ReadLineProgramState::IgnoreSequence;
149 continue;
150 }
151 }
152 }
153 SavedLineProgramRow::Normal {
154 address: row.address(),
155 op_index: row.op_index(),
156 file_index: row.file_index(),
157 line: row.line().map(|nonzero| nonzero.get()).unwrap_or(0),
158 column: match row.column() {
159 gimli::ColumnType::LeftEdge => 0,
160 gimli::ColumnType::Column(val) => val.get(),
161 },
162 discriminator: row.discriminator(),
163 is_stmt: row.is_stmt(),
164 basic_block: row.basic_block(),
165 prologue_end: row.prologue_end(),
166 epilogue_begin: row.epilogue_begin(),
167 isa: row.isa(),
168 }
169 };
170 saved_rows.push((row.address(), saved_row));
171 }
172
173 for FuncRows {
174 index,
175 sorted_rows: saved_rows,
176 } in func_rows
177 {
178 let map = match addr_tr.map().get(index) {
179 Some(map) if map.len > 0 => map,
180 _ => {
181 continue; }
183 };
184 let symbol = map.symbol;
185 let base_addr = map.offset;
186 out_program.begin_sequence(Some(write::Address::Symbol { symbol, addend: 0 }));
187 let mut last_address = None;
189 for addr_map in map.addresses.iter() {
190 let saved_row = match saved_rows.binary_search_by_key(&addr_map.wasm, |i| i.0) {
191 Ok(i) => Some(&saved_rows[i].1),
192 Err(i) => {
193 if i > 0 {
194 Some(&saved_rows[i - 1].1)
195 } else {
196 None
197 }
198 }
199 };
200 if let Some(SavedLineProgramRow::Normal {
201 address,
202 op_index,
203 file_index,
204 line,
205 column,
206 discriminator,
207 is_stmt,
208 basic_block,
209 prologue_end,
210 epilogue_begin,
211 isa,
212 }) = saved_row
213 {
214 if Some(*address) != last_address {
216 let address_offset = if last_address.is_none() {
217 0
220 } else {
221 (addr_map.generated - base_addr) as u64
222 };
223 out_program.row().address_offset = address_offset;
224 out_program.row().op_index = *op_index;
225 out_program.row().file = files[(file_index - file_index_base) as usize];
226 out_program.row().line = *line;
227 out_program.row().column = *column;
228 out_program.row().discriminator = *discriminator;
229 out_program.row().is_statement = *is_stmt;
230 out_program.row().basic_block = *basic_block;
231 out_program.row().prologue_end = *prologue_end;
232 out_program.row().epilogue_begin = *epilogue_begin;
233 out_program.row().isa = *isa;
234 out_program.generate_row();
235 last_address = Some(*address);
236 }
237 }
238 }
239 let end_addr = (map.offset + map.len) as u64;
240 out_program.end_sequence(end_addr);
241 }
242 Ok((out_program, offset, files, file_index_base))
243 } else {
244 Err(TransformError("Valid line program not found").into())
245 }
246}
247
248fn clone_line_string<R>(
249 value: R,
250 form: gimli::DwForm,
251 out_strings: &mut write::StringTable,
252) -> Result<write::LineString, Error>
253where
254 R: Reader,
255{
256 let content = value.to_string_lossy()?.into_owned();
257 Ok(match form {
258 gimli::DW_FORM_strp => {
259 let id = out_strings.add(content);
260 write::LineString::StringRef(id)
261 }
262 gimli::DW_FORM_string => write::LineString::String(content.into()),
263 _ => bail!("DW_FORM_line_strp or other not supported"),
264 })
265}