1use super::expression::{CompiledExpression, FunctionFrameInfo};
2use super::utils::{add_internal_types, append_vmctx_info, get_function_frame_info};
3use super::AddressTransform;
4use crate::read_debuginfo::WasmFileInfo;
5use crate::types::{get_vmctx_value_label, ModuleVmctxInfo, ValueLabelsRanges};
6use anyhow::Error;
7use cranelift_entity::EntityRef;
8use gimli::write;
9use gimli::{self, LineEncoding};
10use std::collections::{HashMap, HashSet};
11use std::path::PathBuf;
12
13pub use crate::read_debuginfo::{DebugInfoData, FunctionMetadata, WasmType};
14
15const PRODUCER_NAME: &str = "wasmtime";
16
17fn generate_line_info(
18 addr_tr: &AddressTransform,
19 translated: &HashSet<u32>,
20 out_encoding: gimli::Encoding,
21 w: &WasmFileInfo,
22 comp_dir_id: write::StringId,
23 name_id: write::StringId,
24 name: &str,
25) -> Result<write::LineProgram, Error> {
26 let out_comp_dir = write::LineString::StringRef(comp_dir_id);
27 let out_comp_name = write::LineString::StringRef(name_id);
28
29 let line_encoding = LineEncoding::default();
30
31 let mut out_program = write::LineProgram::new(
32 out_encoding,
33 line_encoding,
34 out_comp_dir,
35 out_comp_name,
36 None,
37 );
38
39 let file_index = out_program.add_file(
40 write::LineString::String(name.as_bytes().to_vec()),
41 out_program.default_directory(),
42 None,
43 );
44
45 for (i, map) in addr_tr.map() {
46 let symbol = i.index();
47 if translated.contains(&(symbol as u32)) {
48 continue;
49 }
50
51 let base_addr = map.offset;
52 out_program.begin_sequence(Some(write::Address::Symbol { symbol, addend: 0 }));
53 for addr_map in map.addresses.iter() {
54 let address_offset = (addr_map.generated - base_addr) as u64;
55 out_program.row().address_offset = address_offset;
56 out_program.row().op_index = 0;
57 out_program.row().file = file_index;
58 let wasm_offset = w.code_section_offset + addr_map.wasm as u64;
59 out_program.row().line = wasm_offset;
60 out_program.row().column = 0;
61 out_program.row().discriminator = 1;
62 out_program.row().is_statement = true;
63 out_program.row().basic_block = false;
64 out_program.row().prologue_end = false;
65 out_program.row().epilogue_begin = false;
66 out_program.row().isa = 0;
67 out_program.generate_row();
68 }
69 let end_addr = (map.offset + map.len - 1) as u64;
70 out_program.end_sequence(end_addr);
71 }
72
73 Ok(out_program)
74}
75
76fn autogenerate_dwarf_wasm_path(di: &DebugInfoData) -> PathBuf {
77 let module_name = di
78 .name_section
79 .as_ref()
80 .and_then(|ns| ns.module_name.to_owned())
81 .unwrap_or_else(|| unsafe {
82 static mut GEN_ID: u32 = 0;
83 GEN_ID += 1;
84 format!("<gen-{}>", GEN_ID)
85 });
86 let path = format!("/<wasm-module>/{}.wasm", module_name);
87 PathBuf::from(path)
88}
89
90struct WasmTypesDieRefs {
91 vmctx: write::UnitEntryId,
92 i32: write::UnitEntryId,
93 i64: write::UnitEntryId,
94 f32: write::UnitEntryId,
95 f64: write::UnitEntryId,
96}
97
98fn add_wasm_types(
99 unit: &mut write::Unit,
100 root_id: write::UnitEntryId,
101 out_strings: &mut write::StringTable,
102 vmctx_info: &ModuleVmctxInfo,
103) -> WasmTypesDieRefs {
104 let (_wp_die_id, vmctx_die_id) = add_internal_types(unit, root_id, out_strings, vmctx_info);
105
106 macro_rules! def_type {
107 ($id:literal, $size:literal, $enc:path) => {{
108 let die_id = unit.add(root_id, gimli::DW_TAG_base_type);
109 let die = unit.get_mut(die_id);
110 die.set(
111 gimli::DW_AT_name,
112 write::AttributeValue::StringRef(out_strings.add($id)),
113 );
114 die.set(gimli::DW_AT_byte_size, write::AttributeValue::Data1($size));
115 die.set(gimli::DW_AT_encoding, write::AttributeValue::Encoding($enc));
116 die_id
117 }};
118 }
119
120 let i32_die_id = def_type!("i32", 4, gimli::DW_ATE_signed);
121 let i64_die_id = def_type!("i64", 8, gimli::DW_ATE_signed);
122 let f32_die_id = def_type!("f32", 4, gimli::DW_ATE_float);
123 let f64_die_id = def_type!("f64", 8, gimli::DW_ATE_float);
124
125 WasmTypesDieRefs {
126 vmctx: vmctx_die_id,
127 i32: i32_die_id,
128 i64: i64_die_id,
129 f32: f32_die_id,
130 f64: f64_die_id,
131 }
132}
133
134fn resolve_var_type(
135 index: usize,
136 wasm_types: &WasmTypesDieRefs,
137 func_meta: &FunctionMetadata,
138) -> Option<(write::UnitEntryId, bool)> {
139 let (ty, is_param) = if index < func_meta.params.len() {
140 (func_meta.params[index], true)
141 } else {
142 let mut i = (index - func_meta.params.len()) as u32;
143 let mut j = 0;
144 while j < func_meta.locals.len() && i >= func_meta.locals[j].0 {
145 i -= func_meta.locals[j].0;
146 j += 1;
147 }
148 if j >= func_meta.locals.len() {
149 return None;
151 }
152 (func_meta.locals[j].1, false)
153 };
154 let type_die_id = match ty {
155 WasmType::I32 => wasm_types.i32,
156 WasmType::I64 => wasm_types.i64,
157 WasmType::F32 => wasm_types.f32,
158 WasmType::F64 => wasm_types.f64,
159 _ => {
160 return None;
162 }
163 };
164 Some((type_die_id, is_param))
165}
166
167fn generate_vars(
168 unit: &mut write::Unit,
169 die_id: write::UnitEntryId,
170 addr_tr: &AddressTransform,
171 frame_info: &FunctionFrameInfo,
172 scope_ranges: &[(u64, u64)],
173 wasm_types: &WasmTypesDieRefs,
174 func_meta: &FunctionMetadata,
175 locals_names: Option<&HashMap<u32, String>>,
176 out_strings: &mut write::StringTable,
177) {
178 let vmctx_label = get_vmctx_value_label();
179
180 for label in frame_info.value_ranges.keys() {
181 if label.index() == vmctx_label.index() {
182 append_vmctx_info(
183 unit,
184 die_id,
185 wasm_types.vmctx,
186 addr_tr,
187 Some(frame_info),
188 scope_ranges,
189 out_strings,
190 )
191 .expect("append_vmctx_info success");
192 } else {
193 let var_index = label.index();
194 let (type_die_id, is_param) =
195 if let Some(result) = resolve_var_type(var_index, wasm_types, func_meta) {
196 result
197 } else {
198 continue;
200 };
201
202 let loc_list_id = {
203 let endian = gimli::RunTimeEndian::Little;
204
205 let expr = CompiledExpression::from_label(*label);
206 let mut locs = Vec::new();
207 for (begin, length, data) in
208 expr.build_with_locals(scope_ranges, addr_tr, Some(frame_info), endian)
209 {
210 locs.push(write::Location::StartLength {
211 begin,
212 length,
213 data,
214 });
215 }
216 unit.locations.add(write::LocationList(locs))
217 };
218
219 let var_id = unit.add(
220 die_id,
221 if is_param {
222 gimli::DW_TAG_formal_parameter
223 } else {
224 gimli::DW_TAG_variable
225 },
226 );
227 let var = unit.get_mut(var_id);
228
229 let name_id = match locals_names.and_then(|m| m.get(&(var_index as u32))) {
230 Some(n) => out_strings.add(n.to_owned()),
231 None => out_strings.add(format!("var{}", var_index)),
232 };
233
234 var.set(gimli::DW_AT_name, write::AttributeValue::StringRef(name_id));
235 var.set(
236 gimli::DW_AT_type,
237 write::AttributeValue::ThisUnitEntryRef(type_die_id),
238 );
239 var.set(
240 gimli::DW_AT_location,
241 write::AttributeValue::LocationListRef(loc_list_id),
242 );
243 }
244 }
245}
246
247pub fn generate_simulated_dwarf(
248 addr_tr: &AddressTransform,
249 di: &DebugInfoData,
250 vmctx_info: &ModuleVmctxInfo,
251 ranges: &ValueLabelsRanges,
252 translated: &HashSet<u32>,
253 out_encoding: gimli::Encoding,
254 out_units: &mut write::UnitTable,
255 out_strings: &mut write::StringTable,
256) -> Result<(), Error> {
257 let path = di
258 .wasm_file
259 .path
260 .to_owned()
261 .unwrap_or_else(|| autogenerate_dwarf_wasm_path(di));
262
263 let (func_names, locals_names) = if let Some(ref name_section) = di.name_section {
264 (
265 Some(&name_section.func_names),
266 Some(&name_section.locals_names),
267 )
268 } else {
269 (None, None)
270 };
271
272 let (unit, root_id, name_id) = {
273 let comp_dir_id = out_strings.add(path.parent().expect("path dir").to_str().unwrap());
274 let name = path.file_name().expect("path name").to_str().unwrap();
275 let name_id = out_strings.add(name);
276
277 let out_program = generate_line_info(
278 addr_tr,
279 translated,
280 out_encoding,
281 &di.wasm_file,
282 comp_dir_id,
283 name_id,
284 name,
285 )?;
286
287 let unit_id = out_units.add(write::Unit::new(out_encoding, out_program));
288 let unit = out_units.get_mut(unit_id);
289
290 let root_id = unit.root();
291 let root = unit.get_mut(root_id);
292
293 let id = out_strings.add(PRODUCER_NAME);
294 root.set(gimli::DW_AT_producer, write::AttributeValue::StringRef(id));
295 root.set(gimli::DW_AT_name, write::AttributeValue::StringRef(name_id));
296 root.set(
297 gimli::DW_AT_stmt_list,
298 write::AttributeValue::LineProgramRef,
299 );
300 root.set(
301 gimli::DW_AT_comp_dir,
302 write::AttributeValue::StringRef(comp_dir_id),
303 );
304 (unit, root_id, name_id)
305 };
306
307 let wasm_types = add_wasm_types(unit, root_id, out_strings, vmctx_info);
308
309 for (i, map) in addr_tr.map().iter() {
310 let index = i.index();
311 if translated.contains(&(index as u32)) {
312 continue;
313 }
314
315 let start = map.offset as u64;
316 let end = start + map.len as u64;
317 let die_id = unit.add(root_id, gimli::DW_TAG_subprogram);
318 let die = unit.get_mut(die_id);
319 die.set(
320 gimli::DW_AT_low_pc,
321 write::AttributeValue::Address(write::Address::Symbol {
322 symbol: index,
323 addend: start as i64,
324 }),
325 );
326 die.set(
327 gimli::DW_AT_high_pc,
328 write::AttributeValue::Udata((end - start) as u64),
329 );
330
331 let id = match func_names.and_then(|m| m.get(&(index as u32))) {
332 Some(n) => out_strings.add(n.to_owned()),
333 None => out_strings.add(format!("wasm-function[{}]", index)),
334 };
335
336 die.set(gimli::DW_AT_name, write::AttributeValue::StringRef(id));
337
338 die.set(
339 gimli::DW_AT_decl_file,
340 write::AttributeValue::StringRef(name_id),
341 );
342
343 let f = addr_tr.map().get(i).unwrap();
344 let f_start = f.addresses[0].wasm;
345 let wasm_offset = di.wasm_file.code_section_offset + f_start as u64;
346 die.set(
347 gimli::DW_AT_decl_file,
348 write::AttributeValue::Udata(wasm_offset),
349 );
350
351 if let Some(frame_info) = get_function_frame_info(vmctx_info, i, ranges) {
352 let source_range = addr_tr.func_source_range(i);
353 generate_vars(
354 unit,
355 die_id,
356 addr_tr,
357 &frame_info,
358 &[(source_range.0, source_range.1)],
359 &wasm_types,
360 &di.wasm_file.funcs[index],
361 locals_names.and_then(|m| m.get(&(index as u32))),
362 out_strings,
363 );
364 }
365 }
366
367 Ok(())
368}