diff --git a/wasmtime-debug/src/read_debuginfo.rs b/wasmtime-debug/src/read_debuginfo.rs index c39f1d1eb9..fab07ec9eb 100644 --- a/wasmtime-debug/src/read_debuginfo.rs +++ b/wasmtime-debug/src/read_debuginfo.rs @@ -1,5 +1,6 @@ use std::collections::HashMap; -use wasmparser::{ModuleReader, SectionCode}; +use std::path::PathBuf; +use wasmparser::{self, ModuleReader, SectionCode}; use gimli; @@ -13,38 +14,67 @@ trait Reader: gimli::Reader {} impl<'input> Reader for gimli::EndianSlice<'input, LittleEndian> {} +pub use wasmparser::Type as WasmType; + pub type Dwarf<'input> = gimli::Dwarf>; +#[derive(Debug)] +pub struct FunctionMetadata { + pub params: Box<[WasmType]>, + pub locals: Box<[(u32, WasmType)]>, +} + #[derive(Debug)] pub struct WasmFileInfo { + pub path: Option, pub code_section_offset: u64, + pub funcs: Box<[FunctionMetadata]>, +} + +#[derive(Debug)] +pub struct NameSection { + pub module_name: Option, + pub func_names: HashMap, + pub locals_names: HashMap>, } #[derive(Debug)] pub struct DebugInfoData<'a> { pub dwarf: Dwarf<'a>, + pub name_section: Option, pub wasm_file: WasmFileInfo, } fn convert_sections<'a>(sections: HashMap<&str, &'a [u8]>) -> Dwarf<'a> { + const EMPTY_SECTION: &[u8] = &[]; + let endian = LittleEndian; - let debug_str = DebugStr::new(sections[".debug_str"], endian); - let debug_abbrev = DebugAbbrev::new(sections[".debug_abbrev"], endian); - let debug_info = DebugInfo::new(sections[".debug_info"], endian); - let debug_line = DebugLine::new(sections[".debug_line"], endian); + let debug_str = DebugStr::new(sections.get(".debug_str").unwrap_or(&EMPTY_SECTION), endian); + let debug_abbrev = DebugAbbrev::new( + sections.get(".debug_abbrev").unwrap_or(&EMPTY_SECTION), + endian, + ); + let debug_info = DebugInfo::new( + sections.get(".debug_info").unwrap_or(&EMPTY_SECTION), + endian, + ); + let debug_line = DebugLine::new( + sections.get(".debug_line").unwrap_or(&EMPTY_SECTION), + endian, + ); if sections.contains_key(".debug_addr") { panic!("Unexpected .debug_addr"); } - let debug_addr = DebugAddr::from(EndianSlice::new(&[], endian)); + let debug_addr = DebugAddr::from(EndianSlice::new(EMPTY_SECTION, endian)); if sections.contains_key(".debug_line_str") { panic!("Unexpected .debug_line_str"); } - let debug_line_str = DebugLineStr::from(EndianSlice::new(&[], endian)); - let debug_str_sup = DebugStr::from(EndianSlice::new(&[], endian)); + let debug_line_str = DebugLineStr::from(EndianSlice::new(EMPTY_SECTION, endian)); + let debug_str_sup = DebugStr::from(EndianSlice::new(EMPTY_SECTION, endian)); if sections.contains_key(".debug_rnglists") { panic!("Unexpected .debug_rnglists"); @@ -52,9 +82,9 @@ fn convert_sections<'a>(sections: HashMap<&str, &'a [u8]>) -> Dwarf<'a> { let debug_ranges = match sections.get(".debug_ranges") { Some(section) => DebugRanges::new(section, endian), - None => DebugRanges::new(&[], endian), + None => DebugRanges::new(EMPTY_SECTION, endian), }; - let debug_rnglists = DebugRngLists::new(&[], endian); + let debug_rnglists = DebugRngLists::new(EMPTY_SECTION, endian); let ranges = RangeLists::new(debug_ranges, debug_rnglists); if sections.contains_key(".debug_loclists") { @@ -63,22 +93,22 @@ fn convert_sections<'a>(sections: HashMap<&str, &'a [u8]>) -> Dwarf<'a> { let debug_loc = match sections.get(".debug_loc") { Some(section) => DebugLoc::new(section, endian), - None => DebugLoc::new(&[], endian), + None => DebugLoc::new(EMPTY_SECTION, endian), }; - let debug_loclists = DebugLocLists::new(&[], endian); + let debug_loclists = DebugLocLists::new(EMPTY_SECTION, endian); let locations = LocationLists::new(debug_loc, debug_loclists); if sections.contains_key(".debug_str_offsets") { panic!("Unexpected .debug_str_offsets"); } - let debug_str_offsets = DebugStrOffsets::from(EndianSlice::new(&[], endian)); + let debug_str_offsets = DebugStrOffsets::from(EndianSlice::new(EMPTY_SECTION, endian)); if sections.contains_key(".debug_types") { panic!("Unexpected .debug_types"); } - let debug_types = DebugTypes::from(EndianSlice::new(&[], endian)); + let debug_types = DebugTypes::from(EndianSlice::new(EMPTY_SECTION, endian)); Dwarf { debug_abbrev, @@ -95,27 +125,124 @@ fn convert_sections<'a>(sections: HashMap<&str, &'a [u8]>) -> Dwarf<'a> { } } +fn read_name_section(reader: wasmparser::NameSectionReader) -> wasmparser::Result { + let mut module_name = None; + let mut func_names = HashMap::new(); + let mut locals_names = HashMap::new(); + for i in reader.into_iter() { + match i? { + wasmparser::Name::Module(m) => { + module_name = Some(String::from(m.get_name()?)); + } + wasmparser::Name::Function(f) => { + let mut reader = f.get_map()?; + while let Ok(naming) = reader.read() { + func_names.insert(naming.index, String::from(naming.name)); + } + } + wasmparser::Name::Local(l) => { + let mut reader = l.get_function_local_reader()?; + while let Ok(f) = reader.read() { + let mut names = HashMap::new(); + let mut reader = f.get_map()?; + while let Ok(naming) = reader.read() { + names.insert(naming.index, String::from(naming.name)); + } + locals_names.insert(f.func_index, names); + } + } + } + } + let result = NameSection { + module_name, + func_names, + locals_names, + }; + Ok(result) +} + pub fn read_debuginfo(data: &[u8]) -> DebugInfoData { let mut reader = ModuleReader::new(data).expect("reader"); let mut sections = HashMap::new(); + let mut name_section = None; let mut code_section_offset = 0; + + let mut signatures_params: Vec> = Vec::new(); + let mut func_params_refs: Vec = Vec::new(); + let mut func_locals: Vec> = Vec::new(); + while !reader.eof() { let section = reader.read().expect("section"); - if let SectionCode::Custom { name, .. } = section.code { - if name.starts_with(".debug_") { - let mut reader = section.get_binary_reader(); - let len = reader.bytes_remaining(); - sections.insert(name, reader.read_bytes(len).expect("bytes")); + match section.code { + SectionCode::Custom { name, .. } => { + if name.starts_with(".debug_") { + let mut reader = section.get_binary_reader(); + let len = reader.bytes_remaining(); + sections.insert(name, reader.read_bytes(len).expect("bytes")); + } + if name == "name" { + if let Ok(reader) = section.get_name_section_reader() { + if let Ok(section) = read_name_section(reader) { + name_section = Some(section); + } + } + } } - } - if let SectionCode::Code = section.code { - code_section_offset = section.range().start as u64; + SectionCode::Type => { + signatures_params = section + .get_type_section_reader() + .expect("type section") + .into_iter() + .map(|ft| ft.expect("type").params) + .collect::>(); + } + SectionCode::Function => { + func_params_refs = section + .get_function_section_reader() + .expect("function section") + .into_iter() + .map(|index| index.expect("func index") as usize) + .collect::>(); + } + SectionCode::Code => { + code_section_offset = section.range().start as u64; + func_locals = section + .get_code_section_reader() + .expect("code section") + .into_iter() + .map(|body| { + let locals = body + .expect("body") + .get_locals_reader() + .expect("locals reader"); + locals + .into_iter() + .collect::, _>>() + .expect("locals data") + .into_boxed_slice() + }) + .collect::>(); + } + _ => (), } } + + let func_meta = func_params_refs + .into_iter() + .zip(func_locals.into_iter()) + .map(|(params_index, locals)| FunctionMetadata { + params: signatures_params[params_index].clone(), + locals, + }) + .collect::>(); + DebugInfoData { dwarf: convert_sections(sections), + name_section, wasm_file: WasmFileInfo { + path: None, code_section_offset, + funcs: func_meta.into_boxed_slice(), }, } } diff --git a/wasmtime-debug/src/transform/address_transform.rs b/wasmtime-debug/src/transform/address_transform.rs index 8e6715c825..2b0f1558ce 100644 --- a/wasmtime-debug/src/transform/address_transform.rs +++ b/wasmtime-debug/src/transform/address_transform.rs @@ -609,7 +609,9 @@ mod tests { let at = AddressTransform::new( &input, &WasmFileInfo { + path: None, code_section_offset: 1, + funcs: Box::new([]), }, ); diff --git a/wasmtime-debug/src/transform/expression.rs b/wasmtime-debug/src/transform/expression.rs index 19f360f0db..68be19f639 100644 --- a/wasmtime-debug/src/transform/expression.rs +++ b/wasmtime-debug/src/transform/expression.rs @@ -42,9 +42,13 @@ impl Clone for CompiledExpressionPart { impl CompiledExpression { pub fn vmctx() -> CompiledExpression { + CompiledExpression::from_label(get_vmctx_value_label()) + } + + pub fn from_label(label: ValueLabel) -> CompiledExpression { CompiledExpression { parts: vec![ - CompiledExpressionPart::Local(get_vmctx_value_label()), + CompiledExpressionPart::Local(label), CompiledExpressionPart::Code(vec![gimli::constants::DW_OP_stack_value.0 as u8]), ], need_deref: false, diff --git a/wasmtime-debug/src/transform/mod.rs b/wasmtime-debug/src/transform/mod.rs index 94f80908af..f55f1f0f6a 100644 --- a/wasmtime-debug/src/transform/mod.rs +++ b/wasmtime-debug/src/transform/mod.rs @@ -2,6 +2,7 @@ use crate::gc::build_dependencies; use crate::DebugInfoData; use cranelift_codegen::isa::TargetFrontendConfig; use failure::Error; +use simulate::generate_simulated_dwarf; use std::collections::HashSet; use wasmtime_environ::{ModuleAddressMap, ModuleVmctxInfo, ValueLabelsRanges}; @@ -22,7 +23,9 @@ mod attr; mod expression; mod line_program; mod range_info_builder; +mod simulate; mod unit; +mod utils; pub(crate) trait Reader: gimli::Reader {} @@ -78,6 +81,7 @@ pub fn transform_dwarf( let out_line_strings = write::LineStringTable::default(); + let mut translated = HashSet::new(); let mut iter = di.dwarf.debug_info.units(); while let Some(unit) = iter.next().unwrap_or(None) { let unit = di.dwarf.unit(unit)?; @@ -90,9 +94,21 @@ pub fn transform_dwarf( &vmctx_info, &mut out_units, &mut out_strings, + &mut translated, )?; } + generate_simulated_dwarf( + &addr_tr, + di, + &vmctx_info, + &ranges, + &translated, + &out_encoding, + &mut out_units, + &mut out_strings, + )?; + Ok(write::Dwarf { units: out_units, line_programs: vec![], diff --git a/wasmtime-debug/src/transform/simulate.rs b/wasmtime-debug/src/transform/simulate.rs new file mode 100644 index 0000000000..8457fd36a1 --- /dev/null +++ b/wasmtime-debug/src/transform/simulate.rs @@ -0,0 +1,370 @@ +use crate::read_debuginfo::WasmFileInfo; +pub use crate::read_debuginfo::{DebugInfoData, FunctionMetadata, WasmType}; +use cranelift_entity::EntityRef; +use cranelift_wasm::get_vmctx_value_label; +use failure::Error; +use std::collections::{HashMap, HashSet}; +use std::path::PathBuf; +use wasmtime_environ::{ModuleVmctxInfo, ValueLabelsRanges}; + +use gimli::write; +use gimli::{self, LineEncoding}; + +use super::expression::{CompiledExpression, FunctionFrameInfo}; +use super::utils::{add_internal_types, append_vmctx_info, get_function_frame_info}; +use super::AddressTransform; + +const PRODUCER_NAME: &str = "wasmtime"; + +fn generate_line_info( + addr_tr: &AddressTransform, + translated: &HashSet, + out_encoding: &gimli::Encoding, + w: &WasmFileInfo, + comp_dir_id: write::StringId, + name_id: write::StringId, + name: &str, +) -> Result { + let out_comp_dir = write::LineString::StringRef(comp_dir_id); + let out_comp_name = write::LineString::StringRef(name_id); + + let line_encoding = LineEncoding::default(); + + let mut out_program = write::LineProgram::new( + *out_encoding, + line_encoding, + out_comp_dir, + out_comp_name, + None, + ); + + let file_index = out_program.add_file( + write::LineString::String(name.as_bytes().to_vec()), + out_program.default_directory(), + None, + ); + + for (i, map) in addr_tr.map() { + let symbol = i.index(); + if translated.contains(&(symbol as u32)) { + continue; + } + + let base_addr = map.offset; + out_program.begin_sequence(Some(write::Address::Symbol { symbol, addend: 0 })); + for addr_map in map.addresses.iter() { + let address_offset = (addr_map.generated - base_addr) as u64; + out_program.row().address_offset = address_offset; + out_program.row().op_index = 0; + out_program.row().file = file_index; + let wasm_offset = w.code_section_offset + addr_map.wasm as u64; + out_program.row().line = wasm_offset; + out_program.row().column = 0; + out_program.row().discriminator = 1; + out_program.row().is_statement = true; + out_program.row().basic_block = false; + out_program.row().prologue_end = false; + out_program.row().epilogue_begin = false; + out_program.row().isa = 0; + out_program.generate_row(); + } + let end_addr = (map.offset + map.len - 1) as u64; + out_program.end_sequence(end_addr); + } + + Ok(out_program) +} + +fn autogenerate_dwarf_wasm_path(di: &DebugInfoData) -> PathBuf { + let module_name = di + .name_section + .as_ref() + .and_then(|ns| ns.module_name.to_owned()) + .unwrap_or_else(|| unsafe { + static mut GEN_ID: u32 = 0; + GEN_ID += 1; + format!("", GEN_ID) + }); + let path = format!("//{}.wasm", module_name); + PathBuf::from(path) +} + +struct WasmTypesDieRefs { + vmctx: write::UnitEntryId, + i32: write::UnitEntryId, + i64: write::UnitEntryId, + f32: write::UnitEntryId, + f64: write::UnitEntryId, +} + +fn add_wasm_types( + unit: &mut write::Unit, + root_id: write::UnitEntryId, + out_strings: &mut write::StringTable, + vmctx_info: &ModuleVmctxInfo, +) -> WasmTypesDieRefs { + let (_wp_die_id, vmctx_die_id) = add_internal_types(unit, root_id, out_strings, vmctx_info); + + macro_rules! def_type { + ($id:literal, $size:literal, $enc:path) => {{ + let die_id = unit.add(root_id, gimli::DW_TAG_base_type); + let die = unit.get_mut(die_id); + die.set( + gimli::DW_AT_name, + write::AttributeValue::StringRef(out_strings.add($id)), + ); + die.set(gimli::DW_AT_byte_size, write::AttributeValue::Data1($size)); + die.set(gimli::DW_AT_encoding, write::AttributeValue::Encoding($enc)); + die_id + }}; + } + + let i32_die_id = def_type!("i32", 4, gimli::DW_ATE_signed); + let i64_die_id = def_type!("i64", 8, gimli::DW_ATE_signed); + let f32_die_id = def_type!("f32", 4, gimli::DW_ATE_float); + let f64_die_id = def_type!("f64", 8, gimli::DW_ATE_float); + + WasmTypesDieRefs { + vmctx: vmctx_die_id, + i32: i32_die_id, + i64: i64_die_id, + f32: f32_die_id, + f64: f64_die_id, + } +} + +fn resolve_var_type( + index: usize, + wasm_types: &WasmTypesDieRefs, + func_meta: &FunctionMetadata, +) -> Option<(write::UnitEntryId, bool)> { + let (ty, is_param) = if index < func_meta.params.len() { + (func_meta.params[index], true) + } else { + let mut i = (index - func_meta.params.len()) as u32; + let mut j = 0; + while j < func_meta.locals.len() && i >= func_meta.locals[j].0 { + i -= func_meta.locals[j].0; + j += 1; + } + if j >= func_meta.locals.len() { + // Ignore the var index out of bound. + return None; + } + (func_meta.locals[j].1, false) + }; + let type_die_id = match ty { + WasmType::I32 => wasm_types.i32, + WasmType::I64 => wasm_types.i64, + WasmType::F32 => wasm_types.f32, + WasmType::F64 => wasm_types.f64, + _ => { + // Ignore unsupported types. + return None; + } + }; + Some((type_die_id, is_param)) +} + +fn generate_vars( + unit: &mut write::Unit, + die_id: write::UnitEntryId, + addr_tr: &AddressTransform, + frame_info: &FunctionFrameInfo, + scope_ranges: &[(u64, u64)], + wasm_types: &WasmTypesDieRefs, + func_meta: &FunctionMetadata, + locals_names: Option<&HashMap>, + out_strings: &mut write::StringTable, +) { + let vmctx_label = get_vmctx_value_label(); + + for (label, _range) in frame_info.value_ranges { + if label.index() == vmctx_label.index() { + append_vmctx_info( + unit, + die_id, + wasm_types.vmctx, + addr_tr, + Some(frame_info), + scope_ranges, + out_strings, + ) + .expect("append_vmctx_info success"); + } else { + let var_index = label.index(); + let (type_die_id, is_param) = + if let Some(result) = resolve_var_type(var_index, wasm_types, func_meta) { + result + } else { + // Skipping if type of local cannot be detected. + continue; + }; + + let loc_list_id = { + let endian = gimli::RunTimeEndian::Little; + + let expr = CompiledExpression::from_label(label.clone()); + let mut locs = Vec::new(); + for (begin, length, data) in + expr.build_with_locals(scope_ranges, addr_tr, Some(frame_info), endian) + { + locs.push(write::Location::StartLength { + begin, + length, + data, + }); + } + unit.locations.add(write::LocationList(locs)) + }; + + let var_id = unit.add( + die_id, + if is_param { + gimli::DW_TAG_formal_parameter + } else { + gimli::DW_TAG_variable + }, + ); + let var = unit.get_mut(var_id); + + let name_id = match locals_names.and_then(|m| m.get(&(var_index as u32))) { + Some(n) => out_strings.add(n.to_owned()), + None => out_strings.add(format!("var{}", var_index)), + }; + + var.set(gimli::DW_AT_name, write::AttributeValue::StringRef(name_id)); + var.set( + gimli::DW_AT_type, + write::AttributeValue::ThisUnitEntryRef(type_die_id), + ); + var.set( + gimli::DW_AT_location, + write::AttributeValue::LocationListRef(loc_list_id), + ); + } + } +} + +pub fn generate_simulated_dwarf( + addr_tr: &AddressTransform, + di: &DebugInfoData, + vmctx_info: &ModuleVmctxInfo, + ranges: &ValueLabelsRanges, + translated: &HashSet, + out_encoding: &gimli::Encoding, + out_units: &mut write::UnitTable, + out_strings: &mut write::StringTable, +) -> Result<(), Error> { + let path = di + .wasm_file + .path + .to_owned() + .unwrap_or_else(|| autogenerate_dwarf_wasm_path(di)); + + let (func_names, locals_names) = if let Some(ref name_section) = di.name_section { + ( + Some(&name_section.func_names), + Some(&name_section.locals_names), + ) + } else { + (None, None) + }; + + let (unit, root_id, name_id) = { + let comp_dir_id = out_strings.add(path.parent().expect("path dir").to_str().unwrap()); + let name = path.file_name().expect("path name").to_str().unwrap(); + let name_id = out_strings.add(name); + + let out_program = generate_line_info( + addr_tr, + translated, + out_encoding, + &di.wasm_file, + comp_dir_id, + name_id, + name, + )?; + + let unit_id = out_units.add(write::Unit::new(*out_encoding, out_program)); + let unit = out_units.get_mut(unit_id); + + let root_id = unit.root(); + let root = unit.get_mut(root_id); + + let id = out_strings.add(PRODUCER_NAME); + root.set(gimli::DW_AT_producer, write::AttributeValue::StringRef(id)); + root.set(gimli::DW_AT_name, write::AttributeValue::StringRef(name_id)); + root.set( + gimli::DW_AT_stmt_list, + write::AttributeValue::LineProgramRef, + ); + root.set( + gimli::DW_AT_comp_dir, + write::AttributeValue::StringRef(comp_dir_id), + ); + (unit, root_id, name_id) + }; + + let wasm_types = add_wasm_types(unit, root_id, out_strings, vmctx_info); + + for (i, map) in addr_tr.map().iter() { + let index = i.index(); + if translated.contains(&(index as u32)) { + continue; + } + + let start = map.offset as u64; + let end = start + map.len as u64; + let die_id = unit.add(root_id, gimli::DW_TAG_subprogram); + let die = unit.get_mut(die_id); + die.set( + gimli::DW_AT_low_pc, + write::AttributeValue::Address(write::Address::Symbol { + symbol: index, + addend: start as i64, + }), + ); + die.set( + gimli::DW_AT_high_pc, + write::AttributeValue::Udata((end - start) as u64), + ); + + let id = match func_names.and_then(|m| m.get(&(index as u32))) { + Some(n) => out_strings.add(n.to_owned()), + None => out_strings.add(format!("wasm-function[{}]", index)), + }; + + die.set(gimli::DW_AT_name, write::AttributeValue::StringRef(id)); + + die.set( + gimli::DW_AT_decl_file, + write::AttributeValue::StringRef(name_id), + ); + + let f = addr_tr.map().get(i).unwrap(); + let f_start = f.addresses[0].wasm; + let wasm_offset = di.wasm_file.code_section_offset + f_start as u64; + die.set( + gimli::DW_AT_decl_file, + write::AttributeValue::Udata(wasm_offset), + ); + + if let Some(frame_info) = get_function_frame_info(vmctx_info, i, ranges) { + let source_range = addr_tr.func_source_range(i); + generate_vars( + unit, + die_id, + addr_tr, + &frame_info, + &[(source_range.0, source_range.1)], + &wasm_types, + &di.wasm_file.funcs[index], + locals_names.and_then(|m| m.get(&(index as u32))), + out_strings, + ); + } + } + + Ok(()) +} diff --git a/wasmtime-debug/src/transform/unit.rs b/wasmtime-debug/src/transform/unit.rs index f76c9cb159..e4a200476c 100644 --- a/wasmtime-debug/src/transform/unit.rs +++ b/wasmtime-debug/src/transform/unit.rs @@ -1,6 +1,6 @@ -use cranelift_wasm::DefinedFuncIndex; +use cranelift_entity::EntityRef; use failure::Error; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use wasmtime_environ::{ModuleVmctxInfo, ValueLabelsRanges}; use gimli; @@ -11,9 +11,10 @@ use gimli::write; use super::address_transform::AddressTransform; use super::attr::{clone_die_attributes, FileAttributeContext}; -use super::expression::{compile_expression, CompiledExpression, FunctionFrameInfo}; +use super::expression::compile_expression; use super::line_program::clone_line_program; use super::range_info_builder::RangeInfoBuilder; +use super::utils::{add_internal_types, append_vmctx_info, get_function_frame_info}; use super::{DebugInputContext, Reader, TransformError}; pub(crate) type PendingDieRef = (write::UnitEntryId, gimli::DwAt, UnitOffset); @@ -46,109 +47,6 @@ impl InheritedAttr { } } -fn get_function_frame_info<'a, 'b, 'c>( - module_info: &'b ModuleVmctxInfo, - func_index: DefinedFuncIndex, - value_ranges: &'c ValueLabelsRanges, -) -> Option> -where - 'b: 'a, - 'c: 'a, -{ - if let Some(value_ranges) = value_ranges.get(func_index) { - let frame_info = FunctionFrameInfo { - value_ranges, - memory_offset: module_info.memory_offset, - stack_slots: &module_info.stack_slots[func_index], - }; - Some(frame_info) - } else { - None - } -} - -fn add_internal_types( - comp_unit: &mut write::Unit, - root_id: write::UnitEntryId, - out_strings: &mut write::StringTable, - module_info: &ModuleVmctxInfo, -) -> (write::UnitEntryId, write::UnitEntryId) { - let wp_die_id = comp_unit.add(root_id, gimli::DW_TAG_base_type); - let wp_die = comp_unit.get_mut(wp_die_id); - wp_die.set( - gimli::DW_AT_name, - write::AttributeValue::StringRef(out_strings.add("WebAssemblyPtr")), - ); - wp_die.set(gimli::DW_AT_byte_size, write::AttributeValue::Data1(4)); - wp_die.set( - gimli::DW_AT_encoding, - write::AttributeValue::Encoding(gimli::DW_ATE_unsigned), - ); - - let memory_byte_die_id = comp_unit.add(root_id, gimli::DW_TAG_base_type); - let memory_byte_die = comp_unit.get_mut(memory_byte_die_id); - memory_byte_die.set( - gimli::DW_AT_name, - write::AttributeValue::StringRef(out_strings.add("u8")), - ); - memory_byte_die.set( - gimli::DW_AT_encoding, - write::AttributeValue::Encoding(gimli::DW_ATE_unsigned), - ); - memory_byte_die.set(gimli::DW_AT_byte_size, write::AttributeValue::Data1(1)); - - let memory_bytes_die_id = comp_unit.add(root_id, gimli::DW_TAG_pointer_type); - let memory_bytes_die = comp_unit.get_mut(memory_bytes_die_id); - memory_bytes_die.set( - gimli::DW_AT_name, - write::AttributeValue::StringRef(out_strings.add("u8*")), - ); - memory_bytes_die.set( - gimli::DW_AT_type, - write::AttributeValue::ThisUnitEntryRef(memory_byte_die_id), - ); - - let memory_offset = module_info.memory_offset; - let vmctx_die_id = comp_unit.add(root_id, gimli::DW_TAG_structure_type); - let vmctx_die = comp_unit.get_mut(vmctx_die_id); - vmctx_die.set( - gimli::DW_AT_name, - write::AttributeValue::StringRef(out_strings.add("WasmtimeVMContext")), - ); - vmctx_die.set( - gimli::DW_AT_byte_size, - write::AttributeValue::Data4(memory_offset as u32 + 8), - ); - - let m_die_id = comp_unit.add(vmctx_die_id, gimli::DW_TAG_member); - let m_die = comp_unit.get_mut(m_die_id); - m_die.set( - gimli::DW_AT_name, - write::AttributeValue::StringRef(out_strings.add("memory")), - ); - m_die.set( - gimli::DW_AT_type, - write::AttributeValue::ThisUnitEntryRef(memory_bytes_die_id), - ); - m_die.set( - gimli::DW_AT_data_member_location, - write::AttributeValue::Udata(memory_offset as u64), - ); - - let vmctx_ptr_die_id = comp_unit.add(root_id, gimli::DW_TAG_pointer_type); - let vmctx_ptr_die = comp_unit.get_mut(vmctx_ptr_die_id); - vmctx_ptr_die.set( - gimli::DW_AT_name, - write::AttributeValue::StringRef(out_strings.add("WasmtimeVMContext*")), - ); - vmctx_ptr_die.set( - gimli::DW_AT_type, - write::AttributeValue::ThisUnitEntryRef(vmctx_die_id), - ); - - (wp_die_id, vmctx_ptr_die_id) -} - fn get_base_type_name( type_entry: &DebuggingInformationEntry, unit: &Unit, @@ -252,48 +150,6 @@ where Ok(die_id) } -fn append_vmctx_info( - comp_unit: &mut write::Unit, - parent_id: write::UnitEntryId, - vmctx_die_id: write::UnitEntryId, - addr_tr: &AddressTransform, - frame_info: Option<&FunctionFrameInfo>, - scope_ranges: &[(u64, u64)], - out_strings: &mut write::StringTable, -) -> Result<(), Error> { - let loc = { - let endian = gimli::RunTimeEndian::Little; - - let expr = CompiledExpression::vmctx(); - let mut locs = Vec::new(); - for (begin, length, data) in - expr.build_with_locals(scope_ranges, addr_tr, frame_info, endian) - { - locs.push(write::Location::StartLength { - begin, - length, - data, - }); - } - let list_id = comp_unit.locations.add(write::LocationList(locs)); - write::AttributeValue::LocationListRef(list_id) - }; - - let var_die_id = comp_unit.add(parent_id, gimli::DW_TAG_variable); - let var_die = comp_unit.get_mut(var_die_id); - var_die.set( - gimli::DW_AT_name, - write::AttributeValue::StringRef(out_strings.add("__vmctx")), - ); - var_die.set( - gimli::DW_AT_type, - write::AttributeValue::ThisUnitEntryRef(vmctx_die_id), - ); - var_die.set(gimli::DW_AT_location, loc); - - Ok(()) -} - pub(crate) fn clone_unit<'a, R>( unit: Unit, context: &DebugInputContext, @@ -303,6 +159,7 @@ pub(crate) fn clone_unit<'a, R>( module_info: &ModuleVmctxInfo, out_units: &mut write::UnitTable, out_strings: &mut write::StringTable, + translated: &mut HashSet, ) -> Result<(), Error> where R: Reader, @@ -414,6 +271,7 @@ where { current_value_range.push(new_stack_len, frame_info); } + translated.insert(func_index.index() as u32); current_scope_ranges.push(new_stack_len, range_builder.get_ranges(addr_tr)); Some(range_builder) } else { diff --git a/wasmtime-debug/src/transform/utils.rs b/wasmtime-debug/src/transform/utils.rs new file mode 100644 index 0000000000..3ef407fb85 --- /dev/null +++ b/wasmtime-debug/src/transform/utils.rs @@ -0,0 +1,154 @@ +use cranelift_wasm::DefinedFuncIndex; +use failure::Error; +use wasmtime_environ::{ModuleVmctxInfo, ValueLabelsRanges}; + +use gimli; +use gimli::write; + +use super::address_transform::AddressTransform; +use super::expression::{CompiledExpression, FunctionFrameInfo}; + +pub(crate) fn add_internal_types( + comp_unit: &mut write::Unit, + root_id: write::UnitEntryId, + out_strings: &mut write::StringTable, + module_info: &ModuleVmctxInfo, +) -> (write::UnitEntryId, write::UnitEntryId) { + let wp_die_id = comp_unit.add(root_id, gimli::DW_TAG_base_type); + let wp_die = comp_unit.get_mut(wp_die_id); + wp_die.set( + gimli::DW_AT_name, + write::AttributeValue::StringRef(out_strings.add("WebAssemblyPtr")), + ); + wp_die.set(gimli::DW_AT_byte_size, write::AttributeValue::Data1(4)); + wp_die.set( + gimli::DW_AT_encoding, + write::AttributeValue::Encoding(gimli::DW_ATE_unsigned), + ); + + let memory_byte_die_id = comp_unit.add(root_id, gimli::DW_TAG_base_type); + let memory_byte_die = comp_unit.get_mut(memory_byte_die_id); + memory_byte_die.set( + gimli::DW_AT_name, + write::AttributeValue::StringRef(out_strings.add("u8")), + ); + memory_byte_die.set( + gimli::DW_AT_encoding, + write::AttributeValue::Encoding(gimli::DW_ATE_unsigned), + ); + memory_byte_die.set(gimli::DW_AT_byte_size, write::AttributeValue::Data1(1)); + + let memory_bytes_die_id = comp_unit.add(root_id, gimli::DW_TAG_pointer_type); + let memory_bytes_die = comp_unit.get_mut(memory_bytes_die_id); + memory_bytes_die.set( + gimli::DW_AT_name, + write::AttributeValue::StringRef(out_strings.add("u8*")), + ); + memory_bytes_die.set( + gimli::DW_AT_type, + write::AttributeValue::ThisUnitEntryRef(memory_byte_die_id), + ); + + let memory_offset = module_info.memory_offset; + let vmctx_die_id = comp_unit.add(root_id, gimli::DW_TAG_structure_type); + let vmctx_die = comp_unit.get_mut(vmctx_die_id); + vmctx_die.set( + gimli::DW_AT_name, + write::AttributeValue::StringRef(out_strings.add("WasmtimeVMContext")), + ); + vmctx_die.set( + gimli::DW_AT_byte_size, + write::AttributeValue::Data4(memory_offset as u32 + 8), + ); + + let m_die_id = comp_unit.add(vmctx_die_id, gimli::DW_TAG_member); + let m_die = comp_unit.get_mut(m_die_id); + m_die.set( + gimli::DW_AT_name, + write::AttributeValue::StringRef(out_strings.add("memory")), + ); + m_die.set( + gimli::DW_AT_type, + write::AttributeValue::ThisUnitEntryRef(memory_bytes_die_id), + ); + m_die.set( + gimli::DW_AT_data_member_location, + write::AttributeValue::Udata(memory_offset as u64), + ); + + let vmctx_ptr_die_id = comp_unit.add(root_id, gimli::DW_TAG_pointer_type); + let vmctx_ptr_die = comp_unit.get_mut(vmctx_ptr_die_id); + vmctx_ptr_die.set( + gimli::DW_AT_name, + write::AttributeValue::StringRef(out_strings.add("WasmtimeVMContext*")), + ); + vmctx_ptr_die.set( + gimli::DW_AT_type, + write::AttributeValue::ThisUnitEntryRef(vmctx_die_id), + ); + + (wp_die_id, vmctx_ptr_die_id) +} + +pub(crate) fn append_vmctx_info( + comp_unit: &mut write::Unit, + parent_id: write::UnitEntryId, + vmctx_die_id: write::UnitEntryId, + addr_tr: &AddressTransform, + frame_info: Option<&FunctionFrameInfo>, + scope_ranges: &[(u64, u64)], + out_strings: &mut write::StringTable, +) -> Result<(), Error> { + let loc = { + let endian = gimli::RunTimeEndian::Little; + + let expr = CompiledExpression::vmctx(); + let mut locs = Vec::new(); + for (begin, length, data) in + expr.build_with_locals(scope_ranges, addr_tr, frame_info, endian) + { + locs.push(write::Location::StartLength { + begin, + length, + data, + }); + } + let list_id = comp_unit.locations.add(write::LocationList(locs)); + write::AttributeValue::LocationListRef(list_id) + }; + + let var_die_id = comp_unit.add(parent_id, gimli::DW_TAG_variable); + let var_die = comp_unit.get_mut(var_die_id); + var_die.set( + gimli::DW_AT_name, + write::AttributeValue::StringRef(out_strings.add("__vmctx")), + ); + var_die.set( + gimli::DW_AT_type, + write::AttributeValue::ThisUnitEntryRef(vmctx_die_id), + ); + var_die.set(gimli::DW_AT_location, loc); + + Ok(()) +} + +pub(crate) fn get_function_frame_info<'a, 'b, 'c>( + module_info: &'b ModuleVmctxInfo, + func_index: DefinedFuncIndex, + value_ranges: &'c ValueLabelsRanges, +) -> Option> +where + 'b: 'a, + 'c: 'a, +{ + if let Some(value_ranges) = value_ranges.get(func_index) { + let frame_info = FunctionFrameInfo { + value_ranges, + memory_offset: module_info.memory_offset, + stack_slots: &module_info.stack_slots[func_index], + }; + Some(frame_info) + } else { + None + } +}