Utility methods for artificial debug types in the generated DWARF (#1482)
* add operator* * add operator-> * add ptr() unwrap method * comments/refactor * macro_rules * external symbols workaround
This commit is contained in:
@@ -13,7 +13,7 @@ use wasmtime_environ::CacheConfig;
|
|||||||
use wasmtime_environ::Tunables;
|
use wasmtime_environ::Tunables;
|
||||||
use wasmtime_jit::{native, CompilationStrategy, Compiler};
|
use wasmtime_jit::{native, CompilationStrategy, Compiler};
|
||||||
use wasmtime_profiling::{JitDumpAgent, NullProfilerAgent, ProfilingAgent, VTuneAgent};
|
use wasmtime_profiling::{JitDumpAgent, NullProfilerAgent, ProfilingAgent, VTuneAgent};
|
||||||
use wasmtime_runtime::RuntimeMemoryCreator;
|
use wasmtime_runtime::{debug_builtins, RuntimeMemoryCreator};
|
||||||
|
|
||||||
// Runtime Environment
|
// Runtime Environment
|
||||||
|
|
||||||
@@ -465,6 +465,7 @@ impl Engine {
|
|||||||
/// Creates a new [`Engine`] with the specified compilation and
|
/// Creates a new [`Engine`] with the specified compilation and
|
||||||
/// configuration settings.
|
/// configuration settings.
|
||||||
pub fn new(config: &Config) -> Engine {
|
pub fn new(config: &Config) -> Engine {
|
||||||
|
debug_builtins::ensure_exported();
|
||||||
Engine {
|
Engine {
|
||||||
config: Arc::new(config.clone()),
|
config: Arc::new(config.clone()),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,11 +82,23 @@ where
|
|||||||
Ok(String::from("??"))
|
Ok(String::from("??"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Replaces WebAssembly pointer type DIE with the wrapper
|
||||||
|
/// which natively represented by offset in a Wasm memory.
|
||||||
|
///
|
||||||
|
/// `pointer_type_entry` is an DW_TAG_pointer_type entry (e.g. `T*`),
|
||||||
|
/// which refers its base type (e.g. `T`).
|
||||||
|
///
|
||||||
|
/// The generated wrapper is a structure that contains only the
|
||||||
|
/// `__ptr` field. The utility operators overloads is added to
|
||||||
|
/// provide better debugging experience.
|
||||||
|
///
|
||||||
|
/// Notice that "resolve_vmctx_memory_ptr" is external/builtin
|
||||||
|
/// subprogram that is not part of Wasm code.
|
||||||
fn replace_pointer_type<R>(
|
fn replace_pointer_type<R>(
|
||||||
parent_id: write::UnitEntryId,
|
parent_id: write::UnitEntryId,
|
||||||
comp_unit: &mut write::Unit,
|
comp_unit: &mut write::Unit,
|
||||||
wp_die_id: write::UnitEntryId,
|
wp_die_id: write::UnitEntryId,
|
||||||
entry: &DebuggingInformationEntry<R>,
|
pointer_type_entry: &DebuggingInformationEntry<R>,
|
||||||
unit: &Unit<R, R::Offset>,
|
unit: &Unit<R, R::Offset>,
|
||||||
context: &DebugInputContext<R>,
|
context: &DebugInputContext<R>,
|
||||||
out_strings: &mut write::StringTable,
|
out_strings: &mut write::StringTable,
|
||||||
@@ -95,48 +107,121 @@ fn replace_pointer_type<R>(
|
|||||||
where
|
where
|
||||||
R: Reader,
|
R: Reader,
|
||||||
{
|
{
|
||||||
let die_id = comp_unit.add(parent_id, gimli::DW_TAG_structure_type);
|
const WASM_PTR_LEN: u8 = 4;
|
||||||
let die = comp_unit.get_mut(die_id);
|
|
||||||
|
|
||||||
let name = format!(
|
macro_rules! add_tag {
|
||||||
"WebAssemblyPtrWrapper<{}>",
|
($parent_id:ident, $tag:expr => $die:ident as $die_id:ident { $($a:path = $v:expr),* }) => {
|
||||||
get_base_type_name(entry, unit, context)?
|
let $die_id = comp_unit.add($parent_id, $tag);
|
||||||
);
|
#[allow(unused_variables)]
|
||||||
die.set(
|
let $die = comp_unit.get_mut($die_id);
|
||||||
gimli::DW_AT_name,
|
$( $die.set($a, $v); )*
|
||||||
write::AttributeValue::StringRef(out_strings.add(name.as_str())),
|
};
|
||||||
);
|
|
||||||
die.set(gimli::DW_AT_byte_size, write::AttributeValue::Data1(4));
|
|
||||||
|
|
||||||
let p_die_id = comp_unit.add(die_id, gimli::DW_TAG_template_type_parameter);
|
|
||||||
let p_die = comp_unit.get_mut(p_die_id);
|
|
||||||
p_die.set(
|
|
||||||
gimli::DW_AT_name,
|
|
||||||
write::AttributeValue::StringRef(out_strings.add("T")),
|
|
||||||
);
|
|
||||||
p_die.set(
|
|
||||||
gimli::DW_AT_type,
|
|
||||||
write::AttributeValue::ThisUnitEntryRef(wp_die_id),
|
|
||||||
);
|
|
||||||
if let Some(AttributeValue::UnitRef(ref offset)) = entry.attr_value(gimli::DW_AT_type)? {
|
|
||||||
pending_die_refs.insert(p_die_id, gimli::DW_AT_type, *offset);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let m_die_id = comp_unit.add(die_id, gimli::DW_TAG_member);
|
// Build DW_TAG_structure_type for the wrapper:
|
||||||
let m_die = comp_unit.get_mut(m_die_id);
|
// .. DW_AT_name = "WebAssemblyPtrWrapper<T>",
|
||||||
m_die.set(
|
// .. DW_AT_byte_size = 4,
|
||||||
gimli::DW_AT_name,
|
add_tag!(parent_id, gimli::DW_TAG_structure_type => wrapper_die as wrapper_die_id {
|
||||||
write::AttributeValue::StringRef(out_strings.add("__ptr")),
|
gimli::DW_AT_name = write::AttributeValue::StringRef(out_strings.add(format!(
|
||||||
);
|
"WebAssemblyPtrWrapper<{}>",
|
||||||
m_die.set(
|
get_base_type_name(pointer_type_entry, unit, context)?
|
||||||
gimli::DW_AT_type,
|
).as_str())),
|
||||||
write::AttributeValue::ThisUnitEntryRef(wp_die_id),
|
gimli::DW_AT_byte_size = write::AttributeValue::Data1(WASM_PTR_LEN)
|
||||||
);
|
});
|
||||||
m_die.set(
|
|
||||||
gimli::DW_AT_data_member_location,
|
// Build DW_TAG_pointer_type for `WebAssemblyPtrWrapper<T>*`:
|
||||||
write::AttributeValue::Data1(0),
|
// .. DW_AT_type = <wrapper_die>
|
||||||
);
|
add_tag!(parent_id, gimli::DW_TAG_pointer_type => wrapper_ptr_type as wrapper_ptr_type_id {
|
||||||
Ok(die_id)
|
gimli::DW_AT_type = write::AttributeValue::ThisUnitEntryRef(wrapper_die_id)
|
||||||
|
});
|
||||||
|
|
||||||
|
let base_type_id = pointer_type_entry.attr_value(gimli::DW_AT_type)?;
|
||||||
|
// Build DW_TAG_reference_type for `T&`:
|
||||||
|
// .. DW_AT_type = <base_type>
|
||||||
|
add_tag!(parent_id, gimli::DW_TAG_reference_type => ref_type as ref_type_id {});
|
||||||
|
if let Some(AttributeValue::UnitRef(ref offset)) = base_type_id {
|
||||||
|
pending_die_refs.insert(ref_type_id, gimli::DW_AT_type, *offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build DW_TAG_pointer_type for `T*`:
|
||||||
|
// .. DW_AT_type = <base_type>
|
||||||
|
add_tag!(parent_id, gimli::DW_TAG_pointer_type => ptr_type as ptr_type_id {});
|
||||||
|
if let Some(AttributeValue::UnitRef(ref offset)) = base_type_id {
|
||||||
|
pending_die_refs.insert(ptr_type_id, gimli::DW_AT_type, *offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build wrapper_die's DW_TAG_template_type_parameter:
|
||||||
|
// .. DW_AT_name = "T"
|
||||||
|
// .. DW_AT_type = <base_type>
|
||||||
|
add_tag!(wrapper_die_id, gimli::DW_TAG_template_type_parameter => t_param_die as t_param_die_id {
|
||||||
|
gimli::DW_AT_name = write::AttributeValue::StringRef(out_strings.add("T"))
|
||||||
|
});
|
||||||
|
if let Some(AttributeValue::UnitRef(ref offset)) = base_type_id {
|
||||||
|
pending_die_refs.insert(t_param_die_id, gimli::DW_AT_type, *offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build wrapper_die's DW_TAG_member for `__ptr`:
|
||||||
|
// .. DW_AT_name = "__ptr"
|
||||||
|
// .. DW_AT_type = <wp_die>
|
||||||
|
// .. DW_AT_location = 0
|
||||||
|
add_tag!(wrapper_die_id, gimli::DW_TAG_member => m_die as m_die_id {
|
||||||
|
gimli::DW_AT_name = write::AttributeValue::StringRef(out_strings.add("__ptr")),
|
||||||
|
gimli::DW_AT_type = write::AttributeValue::ThisUnitEntryRef(wp_die_id),
|
||||||
|
gimli::DW_AT_data_member_location = write::AttributeValue::Data1(0)
|
||||||
|
});
|
||||||
|
|
||||||
|
// Build wrapper_die's DW_TAG_subprogram for `ptr()`:
|
||||||
|
// .. DW_AT_linkage_name = "resolve_vmctx_memory_ptr"
|
||||||
|
// .. DW_AT_name = "ptr"
|
||||||
|
// .. DW_AT_type = <ptr_type>
|
||||||
|
// .. DW_TAG_formal_parameter
|
||||||
|
// .. .. DW_AT_type = <wrapper_ptr_type>
|
||||||
|
// .. .. DW_AT_artificial = 1
|
||||||
|
add_tag!(wrapper_die_id, gimli::DW_TAG_subprogram => deref_op_die as deref_op_die_id {
|
||||||
|
gimli::DW_AT_linkage_name = write::AttributeValue::StringRef(out_strings.add("resolve_vmctx_memory_ptr")),
|
||||||
|
gimli::DW_AT_name = write::AttributeValue::StringRef(out_strings.add("ptr")),
|
||||||
|
gimli::DW_AT_type = write::AttributeValue::ThisUnitEntryRef(ptr_type_id)
|
||||||
|
});
|
||||||
|
add_tag!(deref_op_die_id, gimli::DW_TAG_formal_parameter => deref_op_this_param as deref_op_this_param_id {
|
||||||
|
gimli::DW_AT_type = write::AttributeValue::ThisUnitEntryRef(wrapper_ptr_type_id),
|
||||||
|
gimli::DW_AT_artificial = write::AttributeValue::Flag(true)
|
||||||
|
});
|
||||||
|
|
||||||
|
// Build wrapper_die's DW_TAG_subprogram for `operator*`:
|
||||||
|
// .. DW_AT_linkage_name = "resolve_vmctx_memory_ptr"
|
||||||
|
// .. DW_AT_name = "operator*"
|
||||||
|
// .. DW_AT_type = <ref_type>
|
||||||
|
// .. DW_TAG_formal_parameter
|
||||||
|
// .. .. DW_AT_type = <wrapper_ptr_type>
|
||||||
|
// .. .. DW_AT_artificial = 1
|
||||||
|
add_tag!(wrapper_die_id, gimli::DW_TAG_subprogram => deref_op_die as deref_op_die_id {
|
||||||
|
gimli::DW_AT_linkage_name = write::AttributeValue::StringRef(out_strings.add("resolve_vmctx_memory_ptr")),
|
||||||
|
gimli::DW_AT_name = write::AttributeValue::StringRef(out_strings.add("operator*")),
|
||||||
|
gimli::DW_AT_type = write::AttributeValue::ThisUnitEntryRef(ref_type_id)
|
||||||
|
});
|
||||||
|
add_tag!(deref_op_die_id, gimli::DW_TAG_formal_parameter => deref_op_this_param as deref_op_this_param_id {
|
||||||
|
gimli::DW_AT_type = write::AttributeValue::ThisUnitEntryRef(wrapper_ptr_type_id),
|
||||||
|
gimli::DW_AT_artificial = write::AttributeValue::Flag(true)
|
||||||
|
});
|
||||||
|
|
||||||
|
// Build wrapper_die's DW_TAG_subprogram for `operator->`:
|
||||||
|
// .. DW_AT_linkage_name = "resolve_vmctx_memory_ptr"
|
||||||
|
// .. DW_AT_name = "operator->"
|
||||||
|
// .. DW_AT_type = <ptr_type>
|
||||||
|
// .. DW_TAG_formal_parameter
|
||||||
|
// .. .. DW_AT_type = <wrapper_ptr_type>
|
||||||
|
// .. .. DW_AT_artificial = 1
|
||||||
|
add_tag!(wrapper_die_id, gimli::DW_TAG_subprogram => deref_op_die as deref_op_die_id {
|
||||||
|
gimli::DW_AT_linkage_name = write::AttributeValue::StringRef(out_strings.add("resolve_vmctx_memory_ptr")),
|
||||||
|
gimli::DW_AT_name = write::AttributeValue::StringRef(out_strings.add("operator->")),
|
||||||
|
gimli::DW_AT_type = write::AttributeValue::ThisUnitEntryRef(ptr_type_id)
|
||||||
|
});
|
||||||
|
add_tag!(deref_op_die_id, gimli::DW_TAG_formal_parameter => deref_op_this_param as deref_op_this_param_id {
|
||||||
|
gimli::DW_AT_type = write::AttributeValue::ThisUnitEntryRef(wrapper_ptr_type_id),
|
||||||
|
gimli::DW_AT_artificial = write::AttributeValue::Flag(true)
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(wrapper_die_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn clone_unit<'a, R>(
|
pub(crate) fn clone_unit<'a, R>(
|
||||||
|
|||||||
@@ -6,49 +6,62 @@ use wasmtime_environ::isa::TargetIsa;
|
|||||||
use wasmtime_environ::wasm::DefinedFuncIndex;
|
use wasmtime_environ::wasm::DefinedFuncIndex;
|
||||||
use wasmtime_environ::{ModuleMemoryOffset, ModuleVmctxInfo, ValueLabelsRanges};
|
use wasmtime_environ::{ModuleMemoryOffset, ModuleVmctxInfo, ValueLabelsRanges};
|
||||||
|
|
||||||
|
/// Adds internal Wasm utility types DIEs such as WebAssemblyPtr and
|
||||||
|
/// WasmtimeVMContext.
|
||||||
|
///
|
||||||
|
/// For unwrapping Wasm pointer, the WasmtimeVMContext has the `set()` method
|
||||||
|
/// that allows to contol current Wasm memory to inspect.
|
||||||
|
/// Notice that "set_vmctx_memory" is an external/builtin subprogram that
|
||||||
|
/// is not part of Wasm code.
|
||||||
pub(crate) fn add_internal_types(
|
pub(crate) fn add_internal_types(
|
||||||
comp_unit: &mut write::Unit,
|
comp_unit: &mut write::Unit,
|
||||||
root_id: write::UnitEntryId,
|
root_id: write::UnitEntryId,
|
||||||
out_strings: &mut write::StringTable,
|
out_strings: &mut write::StringTable,
|
||||||
module_info: &ModuleVmctxInfo,
|
module_info: &ModuleVmctxInfo,
|
||||||
) -> (write::UnitEntryId, write::UnitEntryId) {
|
) -> (write::UnitEntryId, write::UnitEntryId) {
|
||||||
let wp_die_id = comp_unit.add(root_id, gimli::DW_TAG_base_type);
|
const WASM_PTR_LEN: u8 = 4;
|
||||||
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);
|
macro_rules! add_tag {
|
||||||
let memory_byte_die = comp_unit.get_mut(memory_byte_die_id);
|
($parent_id:ident, $tag:expr => $die:ident as $die_id:ident { $($a:path = $v:expr),* }) => {
|
||||||
memory_byte_die.set(
|
let $die_id = comp_unit.add($parent_id, $tag);
|
||||||
gimli::DW_AT_name,
|
let $die = comp_unit.get_mut($die_id);
|
||||||
write::AttributeValue::StringRef(out_strings.add("u8")),
|
$( $die.set($a, $v); )*
|
||||||
);
|
};
|
||||||
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);
|
// Build DW_TAG_base_type for generic `WebAssemblyPtr`.
|
||||||
let memory_bytes_die = comp_unit.get_mut(memory_bytes_die_id);
|
// .. DW_AT_name = "WebAssemblyPtr"
|
||||||
memory_bytes_die.set(
|
// .. DW_AT_byte_size = 4
|
||||||
gimli::DW_AT_name,
|
// .. DW_AT_encoding = DW_ATE_unsigned
|
||||||
write::AttributeValue::StringRef(out_strings.add("u8*")),
|
// let wp_die_id = comp_unit.add(root_id, gimli::DW_TAG_base_type);
|
||||||
);
|
// let wp_die = comp_unit.get_mut(wp_die_id);
|
||||||
memory_bytes_die.set(
|
add_tag!(root_id, gimli::DW_TAG_base_type => wp_die as wp_die_id {
|
||||||
gimli::DW_AT_type,
|
gimli::DW_AT_name = write::AttributeValue::StringRef(out_strings.add("WebAssemblyPtr")),
|
||||||
write::AttributeValue::ThisUnitEntryRef(memory_byte_die_id),
|
gimli::DW_AT_byte_size = write::AttributeValue::Data1(WASM_PTR_LEN),
|
||||||
);
|
gimli::DW_AT_encoding = write::AttributeValue::Encoding(gimli::DW_ATE_unsigned)
|
||||||
|
});
|
||||||
|
|
||||||
|
// Build DW_TAG_base_type for Wasm byte:
|
||||||
|
// .. DW_AT_name = u8
|
||||||
|
// .. DW_AT_encoding = DW_ATE_unsigned
|
||||||
|
// .. DW_AT_byte_size = 1
|
||||||
|
add_tag!(root_id, gimli::DW_TAG_base_type => memory_byte_die as memory_byte_die_id {
|
||||||
|
gimli::DW_AT_name = write::AttributeValue::StringRef(out_strings.add("u8")),
|
||||||
|
gimli::DW_AT_encoding = write::AttributeValue::Encoding(gimli::DW_ATE_unsigned),
|
||||||
|
gimli::DW_AT_byte_size = write::AttributeValue::Data1(1)
|
||||||
|
});
|
||||||
|
|
||||||
|
// Build DW_TAG_pointer_type that references Wasm bytes:
|
||||||
|
// .. DW_AT_name = "u8*"
|
||||||
|
// .. DW_AT_type = <memory_byte_die>
|
||||||
|
add_tag!(root_id, gimli::DW_TAG_pointer_type => memory_bytes_die as memory_bytes_die_id {
|
||||||
|
gimli::DW_AT_name = write::AttributeValue::StringRef(out_strings.add("u8*")),
|
||||||
|
gimli::DW_AT_type = write::AttributeValue::ThisUnitEntryRef(memory_byte_die_id)
|
||||||
|
});
|
||||||
|
|
||||||
// Create artificial VMContext type and its reference for convinience viewing
|
// Create artificial VMContext type and its reference for convinience viewing
|
||||||
// its fields (such as memory ref) in a debugger.
|
// its fields (such as memory ref) in a debugger. Build DW_TAG_structure_type:
|
||||||
|
// .. DW_AT_name = "WasmtimeVMContext"
|
||||||
let vmctx_die_id = comp_unit.add(root_id, gimli::DW_TAG_structure_type);
|
let vmctx_die_id = comp_unit.add(root_id, gimli::DW_TAG_structure_type);
|
||||||
let vmctx_die = comp_unit.get_mut(vmctx_die_id);
|
let vmctx_die = comp_unit.get_mut(vmctx_die_id);
|
||||||
vmctx_die.set(
|
vmctx_die.set(
|
||||||
@@ -56,6 +69,7 @@ pub(crate) fn add_internal_types(
|
|||||||
write::AttributeValue::StringRef(out_strings.add("WasmtimeVMContext")),
|
write::AttributeValue::StringRef(out_strings.add("WasmtimeVMContext")),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// TODO multiple memories
|
||||||
match module_info.memory_offset {
|
match module_info.memory_offset {
|
||||||
ModuleMemoryOffset::Defined(memory_offset) => {
|
ModuleMemoryOffset::Defined(memory_offset) => {
|
||||||
// The context has defined memory: extend the WasmtimeVMContext size
|
// The context has defined memory: extend the WasmtimeVMContext size
|
||||||
@@ -67,20 +81,15 @@ pub(crate) fn add_internal_types(
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Define the "memory" field which is a direct pointer to allocated Wasm memory.
|
// Define the "memory" field which is a direct pointer to allocated Wasm memory.
|
||||||
let m_die_id = comp_unit.add(vmctx_die_id, gimli::DW_TAG_member);
|
// Build DW_TAG_member:
|
||||||
let m_die = comp_unit.get_mut(m_die_id);
|
// .. DW_AT_name = "memory"
|
||||||
m_die.set(
|
// .. DW_AT_type = <memory_bytes_die>
|
||||||
gimli::DW_AT_name,
|
// .. DW_AT_data_member_location = `memory_offset`
|
||||||
write::AttributeValue::StringRef(out_strings.add("memory")),
|
add_tag!(vmctx_die_id, gimli::DW_TAG_member => m_die as m_die_id {
|
||||||
);
|
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),
|
||||||
gimli::DW_AT_type,
|
gimli::DW_AT_data_member_location = write::AttributeValue::Udata(memory_offset as u64)
|
||||||
write::AttributeValue::ThisUnitEntryRef(memory_bytes_die_id),
|
});
|
||||||
);
|
|
||||||
m_die.set(
|
|
||||||
gimli::DW_AT_data_member_location,
|
|
||||||
write::AttributeValue::Udata(memory_offset as u64),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
ModuleMemoryOffset::Imported(_) => {
|
ModuleMemoryOffset::Imported(_) => {
|
||||||
// TODO implement convinience pointer to and additional types for VMMemoryImport.
|
// TODO implement convinience pointer to and additional types for VMMemoryImport.
|
||||||
@@ -88,16 +97,28 @@ pub(crate) fn add_internal_types(
|
|||||||
ModuleMemoryOffset::None => (),
|
ModuleMemoryOffset::None => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
let vmctx_ptr_die_id = comp_unit.add(root_id, gimli::DW_TAG_pointer_type);
|
// Build DW_TAG_pointer_type for `WasmtimeVMContext*`:
|
||||||
let vmctx_ptr_die = comp_unit.get_mut(vmctx_ptr_die_id);
|
// .. DW_AT_name = "WasmtimeVMContext*"
|
||||||
vmctx_ptr_die.set(
|
// .. DW_AT_type = <vmctx_die>
|
||||||
gimli::DW_AT_name,
|
add_tag!(root_id, gimli::DW_TAG_pointer_type => vmctx_ptr_die as vmctx_ptr_die_id {
|
||||||
write::AttributeValue::StringRef(out_strings.add("WasmtimeVMContext*")),
|
gimli::DW_AT_name = write::AttributeValue::StringRef(out_strings.add("WasmtimeVMContext*")),
|
||||||
);
|
gimli::DW_AT_type = write::AttributeValue::ThisUnitEntryRef(vmctx_die_id)
|
||||||
vmctx_ptr_die.set(
|
});
|
||||||
gimli::DW_AT_type,
|
|
||||||
write::AttributeValue::ThisUnitEntryRef(vmctx_die_id),
|
// Build vmctx_die's DW_TAG_subprogram for `set` method:
|
||||||
);
|
// .. DW_AT_linkage_name = "set_vmctx_memory"
|
||||||
|
// .. DW_AT_name = "set"
|
||||||
|
// .. DW_TAG_formal_parameter
|
||||||
|
// .. .. DW_AT_type = <vmctx_ptr_die>
|
||||||
|
// .. .. DW_AT_artificial = 1
|
||||||
|
add_tag!(vmctx_die_id, gimli::DW_TAG_subprogram => vmctx_set as vmctx_set_id {
|
||||||
|
gimli::DW_AT_linkage_name = write::AttributeValue::StringRef(out_strings.add("set_vmctx_memory")),
|
||||||
|
gimli::DW_AT_name = write::AttributeValue::StringRef(out_strings.add("set"))
|
||||||
|
});
|
||||||
|
add_tag!(vmctx_set_id, gimli::DW_TAG_formal_parameter => vmctx_set_this_param as vmctx_set_this_param_id {
|
||||||
|
gimli::DW_AT_type = write::AttributeValue::ThisUnitEntryRef(vmctx_ptr_die_id),
|
||||||
|
gimli::DW_AT_artificial = write::AttributeValue::Flag(true)
|
||||||
|
});
|
||||||
|
|
||||||
(wp_die_id, vmctx_ptr_die_id)
|
(wp_die_id, vmctx_ptr_die_id)
|
||||||
}
|
}
|
||||||
|
|||||||
41
crates/runtime/src/debug_builtins.rs
Normal file
41
crates/runtime/src/debug_builtins.rs
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
#![doc(hidden)]
|
||||||
|
|
||||||
|
use crate::instance::InstanceHandle;
|
||||||
|
use crate::vmcontext::VMContext;
|
||||||
|
use wasmtime_environ::entity::EntityRef;
|
||||||
|
use wasmtime_environ::wasm::MemoryIndex;
|
||||||
|
|
||||||
|
static mut VMCTX_AND_MEMORY: (*mut VMContext, usize) = (std::ptr::null_mut(), 0);
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn resolve_vmctx_memory_ptr(p: *const u32) -> *const u8 {
|
||||||
|
let ptr = std::ptr::read(p);
|
||||||
|
assert!(
|
||||||
|
!VMCTX_AND_MEMORY.0.is_null(),
|
||||||
|
"must call `__vmctx->set()` before resolving Wasm pointers"
|
||||||
|
);
|
||||||
|
let handle = InstanceHandle::from_vmctx(VMCTX_AND_MEMORY.0);
|
||||||
|
assert!(
|
||||||
|
VMCTX_AND_MEMORY.1 < handle.instance().module().local.memory_plans.len(),
|
||||||
|
"memory index for debugger is out of bounds"
|
||||||
|
);
|
||||||
|
let index = MemoryIndex::new(VMCTX_AND_MEMORY.1);
|
||||||
|
let mem = handle.instance().get_memory(index);
|
||||||
|
mem.base.add(ptr as usize)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn set_vmctx_memory(vmctx_ptr: *mut VMContext) {
|
||||||
|
// TODO multi-memory
|
||||||
|
VMCTX_AND_MEMORY = (vmctx_ptr, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensures that set_vmctx_memory and resolve_vmctx_memory_ptr are linked and
|
||||||
|
// exported as symbols. It is a workaround: the executable normally ignores
|
||||||
|
// `pub extern "C"`, see rust-lang/rust#25057.
|
||||||
|
pub fn ensure_exported() {
|
||||||
|
unsafe {
|
||||||
|
std::ptr::read_volatile(resolve_vmctx_memory_ptr as *const u8);
|
||||||
|
std::ptr::read_volatile(set_vmctx_memory as *const u8);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -33,6 +33,7 @@ mod trap_registry;
|
|||||||
mod traphandlers;
|
mod traphandlers;
|
||||||
mod vmcontext;
|
mod vmcontext;
|
||||||
|
|
||||||
|
pub mod debug_builtins;
|
||||||
pub mod libcalls;
|
pub mod libcalls;
|
||||||
|
|
||||||
pub use crate::export::*;
|
pub use crate::export::*;
|
||||||
|
|||||||
@@ -92,3 +92,37 @@ check: exited with status
|
|||||||
)?;
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
#[cfg(all(
|
||||||
|
any(target_os = "linux", target_os = "macos"),
|
||||||
|
target_pointer_width = "64"
|
||||||
|
))]
|
||||||
|
pub fn test_debug_dwarf_ptr() -> Result<()> {
|
||||||
|
let output = lldb_with_script(
|
||||||
|
&[
|
||||||
|
"-g",
|
||||||
|
"--opt-level",
|
||||||
|
"0",
|
||||||
|
"tests/debug/testsuite/reverse-str.wasm",
|
||||||
|
],
|
||||||
|
r#"b reverse-str.c:9
|
||||||
|
r
|
||||||
|
p __vmctx->set(),&*s
|
||||||
|
c"#,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
check_lldb_output(
|
||||||
|
&output,
|
||||||
|
r#"
|
||||||
|
check: Breakpoint 1: no locations (pending)
|
||||||
|
check: stop reason = breakpoint 1.1
|
||||||
|
check: frame #0
|
||||||
|
sameln: reverse(s=(__ptr =
|
||||||
|
check: "Hello, world."
|
||||||
|
check: resuming
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|||||||
21
tests/debug/testsuite/reverse-str.c
Normal file
21
tests/debug/testsuite/reverse-str.c
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
// Compile with:
|
||||||
|
// clang --target=wasm32 reverse-str.c -o reverse-str.wasm -g \
|
||||||
|
// -O0 -nostdlib -fdebug-prefix-map=$PWD=.
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
void reverse(char *s, size_t len)
|
||||||
|
{
|
||||||
|
if (!len) return;
|
||||||
|
size_t i = 0, j = len - 1;
|
||||||
|
while (i < j) {
|
||||||
|
char t = s[i];
|
||||||
|
s[i++] = s[j];
|
||||||
|
s[j--] = t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _start()
|
||||||
|
{
|
||||||
|
char hello[] = "Hello, world.";
|
||||||
|
reverse(hello, 13);
|
||||||
|
}
|
||||||
BIN
tests/debug/testsuite/reverse-str.wasm
Executable file
BIN
tests/debug/testsuite/reverse-str.wasm
Executable file
Binary file not shown.
Reference in New Issue
Block a user