Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/crates/cranelift/src/debug/transform/line_program.rs
1693 views
1
use super::address_transform::AddressTransform;
2
use super::{Reader, TransformError};
3
use anyhow::{Error, bail};
4
use gimli::{DebugLineOffset, LineEncoding, Unit, write};
5
use wasmtime_environ::DefinedFuncIndex;
6
7
#[derive(Debug)]
8
enum 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)]
26
struct FuncRows {
27
index: DefinedFuncIndex,
28
sorted_rows: Vec<(u64, SavedLineProgramRow)>,
29
}
30
31
#[derive(Debug, Eq, PartialEq)]
32
enum ReadLineProgramState {
33
SequenceEnded,
34
ReadSequence(DefinedFuncIndex),
35
IgnoreSequence,
36
}
37
38
pub(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>
46
where
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
// Since we are outputting DWARF-4, perform base change.
94
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
// Discard sequences for non-existent code.
138
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
// Some non-existent address found.
148
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; // no code generated
182
}
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
// TODO track and place function declaration line here
188
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
// Ignore duplicates
215
if Some(*address) != last_address {
216
let address_offset = if last_address.is_none() {
217
// Extend first entry to the function declaration
218
// TODO use the function declaration line instead
219
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
248
fn clone_line_string<R>(
249
value: R,
250
form: gimli::DwForm,
251
out_strings: &mut write::StringTable,
252
) -> Result<write::LineString, Error>
253
where
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
}
266
267