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