Generate debug info for LLDB/GDB (#50)
* Transform DWARF sections into native format for wasm2obj and wasmtime. Generate DWARF sections based on WASM DWARF. Ignore some of debug_info/debug_line for dead code. * Fix test
This commit is contained in:
committed by
Dan Gohman
parent
6eb09d9edd
commit
ddbc00752e
@@ -18,6 +18,7 @@ cranelift-wasm = "0.28.0"
|
||||
cranelift-frontend = "0.28.0"
|
||||
wasmtime-environ = { path = "../environ", default-features = false }
|
||||
wasmtime-runtime = { path = "../runtime", default-features = false }
|
||||
wasmtime-debug = { path = "../debug", default-features = false }
|
||||
region = "2.0.0"
|
||||
failure = { version = "0.1.3", default-features = false }
|
||||
failure_derive = { version = "0.1.3", default-features = false }
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
use core::{cmp, mem};
|
||||
use region;
|
||||
use std::boxed::Box;
|
||||
use std::string::String;
|
||||
use std::vec::Vec;
|
||||
use wasmtime_runtime::{Mmap, VMFunctionBody};
|
||||
@@ -63,6 +64,25 @@ impl CodeMemory {
|
||||
Ok(Self::view_as_mut_vmfunc_slice(new))
|
||||
}
|
||||
|
||||
/// Allocate enough continuous memory block for multiple code blocks. See also
|
||||
/// allocate_copy_of_byte_slice.
|
||||
pub fn allocate_copy_of_byte_slices(
|
||||
&mut self,
|
||||
slices: &[&[u8]],
|
||||
) -> Result<Box<[&mut [VMFunctionBody]]>, String> {
|
||||
let total_len = slices.into_iter().fold(0, |acc, slice| acc + slice.len());
|
||||
let new = self.allocate(total_len)?;
|
||||
let mut tail = new;
|
||||
let mut result = Vec::with_capacity(slices.len());
|
||||
for slice in slices {
|
||||
let (block, next_tail) = tail.split_at_mut(slice.len());
|
||||
block.copy_from_slice(slice);
|
||||
tail = next_tail;
|
||||
result.push(Self::view_as_mut_vmfunc_slice(block));
|
||||
}
|
||||
Ok(result.into_boxed_slice())
|
||||
}
|
||||
|
||||
/// Make all allocated memory executable.
|
||||
pub fn publish(&mut self) {
|
||||
self.mmaps
|
||||
|
||||
@@ -14,6 +14,7 @@ use cranelift_wasm::DefinedFuncIndex;
|
||||
use std::boxed::Box;
|
||||
use std::string::String;
|
||||
use std::vec::Vec;
|
||||
use wasmtime_debug::{emit_debugsections_image, DebugInfoData};
|
||||
use wasmtime_environ::cranelift;
|
||||
use wasmtime_environ::{Compilation, CompileError, Module, Relocations, Tunables};
|
||||
use wasmtime_runtime::{InstantiationError, SignatureRegistry, VMFunctionBody};
|
||||
@@ -66,26 +67,54 @@ impl Compiler {
|
||||
&mut self,
|
||||
module: &Module,
|
||||
function_body_inputs: PrimaryMap<DefinedFuncIndex, &'data [u8]>,
|
||||
debug_data: Option<DebugInfoData>,
|
||||
) -> Result<
|
||||
(
|
||||
PrimaryMap<DefinedFuncIndex, *mut [VMFunctionBody]>,
|
||||
Relocations,
|
||||
Option<Vec<u8>>,
|
||||
),
|
||||
SetupError,
|
||||
> {
|
||||
let (compilation, relocations) =
|
||||
cranelift::compile_module(module, function_body_inputs, &*self.isa)
|
||||
.map_err(SetupError::Compile)?;
|
||||
let (compilation, relocations, address_transform) = cranelift::compile_module(
|
||||
module,
|
||||
function_body_inputs,
|
||||
&*self.isa,
|
||||
debug_data.is_some(),
|
||||
)
|
||||
.map_err(SetupError::Compile)?;
|
||||
|
||||
let allocated_functions =
|
||||
allocate_functions(&mut self.code_memory, compilation).map_err(|message| {
|
||||
allocate_functions(&mut self.code_memory, &compilation).map_err(|message| {
|
||||
SetupError::Instantiate(InstantiationError::Resource(format!(
|
||||
"failed to allocate memory for functions: {}",
|
||||
message
|
||||
)))
|
||||
})?;
|
||||
|
||||
Ok((allocated_functions, relocations))
|
||||
let dbg = if let Some(debug_data) = debug_data {
|
||||
let target_config = self.isa.frontend_config();
|
||||
let triple = self.isa.triple().clone();
|
||||
let mut funcs = Vec::new();
|
||||
for (i, allocated) in allocated_functions.into_iter() {
|
||||
let ptr = (*allocated) as *const u8;
|
||||
let body_len = compilation.functions[i].len();
|
||||
funcs.push((ptr, body_len));
|
||||
}
|
||||
let bytes = emit_debugsections_image(
|
||||
triple,
|
||||
&target_config,
|
||||
&debug_data,
|
||||
&address_transform,
|
||||
&funcs,
|
||||
)
|
||||
.map_err(|e| SetupError::DebugInfo(e))?;
|
||||
Some(bytes)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok((allocated_functions, relocations, dbg))
|
||||
}
|
||||
|
||||
/// Create a trampoline for invoking a function.
|
||||
@@ -219,12 +248,22 @@ fn make_trampoline(
|
||||
|
||||
fn allocate_functions(
|
||||
code_memory: &mut CodeMemory,
|
||||
compilation: Compilation,
|
||||
compilation: &Compilation,
|
||||
) -> Result<PrimaryMap<DefinedFuncIndex, *mut [VMFunctionBody]>, String> {
|
||||
// Allocate code for all function in one continuous memory block.
|
||||
// First, collect all function bodies into vector to pass to the
|
||||
// allocate_copy_of_byte_slices.
|
||||
let bodies = compilation
|
||||
.functions
|
||||
.values()
|
||||
.map(|body| body.as_slice())
|
||||
.collect::<Vec<&[u8]>>();
|
||||
let fat_ptrs = code_memory.allocate_copy_of_byte_slices(&bodies)?;
|
||||
// Second, create a PrimaryMap from result vector of pointers.
|
||||
let mut result = PrimaryMap::with_capacity(compilation.functions.len());
|
||||
for (_, body) in compilation.functions.into_iter() {
|
||||
let fatptr: *mut [VMFunctionBody] = code_memory.allocate_copy_of_byte_slice(body)?;
|
||||
result.push(fatptr);
|
||||
for i in 0..fat_ptrs.len() {
|
||||
let fat_ptr: *mut [VMFunctionBody] = fat_ptrs[i];
|
||||
result.push(fat_ptr);
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ pub struct Context {
|
||||
namespace: Namespace,
|
||||
compiler: Box<Compiler>,
|
||||
global_exports: Rc<RefCell<HashMap<String, Option<wasmtime_runtime::Export>>>>,
|
||||
debug_info: bool,
|
||||
}
|
||||
|
||||
impl Context {
|
||||
@@ -57,9 +58,20 @@ impl Context {
|
||||
namespace: Namespace::new(),
|
||||
compiler,
|
||||
global_exports: Rc::new(RefCell::new(HashMap::new())),
|
||||
debug_info: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get debug_info settings.
|
||||
pub fn debug_info(&self) -> bool {
|
||||
self.debug_info
|
||||
}
|
||||
|
||||
/// Set debug_info settings.
|
||||
pub fn set_debug_info(&mut self, value: bool) {
|
||||
self.debug_info = value;
|
||||
}
|
||||
|
||||
/// Construct a new instance of `Context` with the given target.
|
||||
pub fn with_isa(isa: Box<TargetIsa>) -> Self {
|
||||
Self::new(Box::new(Compiler::new(isa)))
|
||||
@@ -88,12 +100,14 @@ impl Context {
|
||||
|
||||
fn instantiate(&mut self, data: &[u8]) -> Result<InstanceHandle, SetupError> {
|
||||
self.validate(&data).map_err(SetupError::Validate)?;
|
||||
let debug_info = self.debug_info();
|
||||
|
||||
instantiate(
|
||||
&mut *self.compiler,
|
||||
&data,
|
||||
&mut self.namespace,
|
||||
Rc::clone(&self.global_exports),
|
||||
debug_info,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -11,14 +11,17 @@ use core::cell::RefCell;
|
||||
use cranelift_entity::{BoxedSlice, PrimaryMap};
|
||||
use cranelift_wasm::{DefinedFuncIndex, SignatureIndex};
|
||||
use std::boxed::Box;
|
||||
use std::io::Write;
|
||||
use std::rc::Rc;
|
||||
use std::string::String;
|
||||
use std::vec::Vec;
|
||||
use wasmtime_debug::read_debuginfo;
|
||||
use wasmtime_environ::{
|
||||
CompileError, DataInitializer, DataInitializerLocation, Module, ModuleEnvironment,
|
||||
};
|
||||
use wasmtime_runtime::{
|
||||
Export, Imports, InstanceHandle, InstantiationError, VMFunctionBody, VMSharedSignatureIndex,
|
||||
Export, GdbJitImageRegistration, Imports, InstanceHandle, InstantiationError, VMFunctionBody,
|
||||
VMSharedSignatureIndex,
|
||||
};
|
||||
|
||||
/// An error condition while setting up a wasm instance, be it validation,
|
||||
@@ -37,6 +40,10 @@ pub enum SetupError {
|
||||
/// trapped.
|
||||
#[fail(display = "Instantiation error: {}", _0)]
|
||||
Instantiate(InstantiationError),
|
||||
|
||||
/// Debug information generation error occured.
|
||||
#[fail(display = "Debug information error: {}", _0)]
|
||||
DebugInfo(failure::Error),
|
||||
}
|
||||
|
||||
/// This is similar to `CompiledModule`, but references the data initializers
|
||||
@@ -47,6 +54,7 @@ struct RawCompiledModule<'data> {
|
||||
imports: Imports,
|
||||
data_initializers: Box<[DataInitializer<'data>]>,
|
||||
signatures: BoxedSlice<SignatureIndex, VMSharedSignatureIndex>,
|
||||
dbg_jit_registration: Option<GdbJitImageRegistration>,
|
||||
}
|
||||
|
||||
impl<'data> RawCompiledModule<'data> {
|
||||
@@ -55,6 +63,7 @@ impl<'data> RawCompiledModule<'data> {
|
||||
compiler: &mut Compiler,
|
||||
data: &'data [u8],
|
||||
resolver: &mut dyn Resolver,
|
||||
debug_info: bool,
|
||||
) -> Result<Self, SetupError> {
|
||||
let environ = ModuleEnvironment::new(compiler.frontend_config(), compiler.tunables());
|
||||
|
||||
@@ -62,8 +71,17 @@ impl<'data> RawCompiledModule<'data> {
|
||||
.translate(data)
|
||||
.map_err(|error| SetupError::Compile(CompileError::Wasm(error)))?;
|
||||
|
||||
let (allocated_functions, relocations) =
|
||||
compiler.compile(&translation.module, translation.function_body_inputs)?;
|
||||
let debug_data = if debug_info {
|
||||
Some(read_debuginfo(&data))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let (allocated_functions, relocations, dbg_image) = compiler.compile(
|
||||
&translation.module,
|
||||
translation.function_body_inputs,
|
||||
debug_data,
|
||||
)?;
|
||||
|
||||
let imports = link_module(
|
||||
&translation.module,
|
||||
@@ -98,12 +116,22 @@ impl<'data> RawCompiledModule<'data> {
|
||||
// Make all code compiled thus far executable.
|
||||
compiler.publish_compiled_code();
|
||||
|
||||
let dbg_jit_registration = if let Some(img) = dbg_image {
|
||||
let mut bytes = Vec::new();
|
||||
bytes.write_all(&img).expect("all written");
|
||||
let reg = GdbJitImageRegistration::register(bytes);
|
||||
Some(reg)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
module: translation.module,
|
||||
finished_functions,
|
||||
imports,
|
||||
data_initializers: translation.data_initializers.into_boxed_slice(),
|
||||
signatures: signatures.into_boxed_slice(),
|
||||
dbg_jit_registration,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -116,6 +144,7 @@ pub struct CompiledModule {
|
||||
data_initializers: Box<[OwnedDataInitializer]>,
|
||||
signatures: BoxedSlice<SignatureIndex, VMSharedSignatureIndex>,
|
||||
global_exports: Rc<RefCell<HashMap<String, Option<Export>>>>,
|
||||
dbg_jit_registration: Option<Rc<GdbJitImageRegistration>>,
|
||||
}
|
||||
|
||||
impl CompiledModule {
|
||||
@@ -125,8 +154,9 @@ impl CompiledModule {
|
||||
data: &'data [u8],
|
||||
resolver: &mut dyn Resolver,
|
||||
global_exports: Rc<RefCell<HashMap<String, Option<Export>>>>,
|
||||
debug_info: bool,
|
||||
) -> Result<Self, SetupError> {
|
||||
let raw = RawCompiledModule::<'data>::new(compiler, data, resolver)?;
|
||||
let raw = RawCompiledModule::<'data>::new(compiler, data, resolver, debug_info)?;
|
||||
|
||||
Ok(Self::from_parts(
|
||||
raw.module,
|
||||
@@ -139,6 +169,7 @@ impl CompiledModule {
|
||||
.collect::<Vec<_>>()
|
||||
.into_boxed_slice(),
|
||||
raw.signatures.clone(),
|
||||
raw.dbg_jit_registration,
|
||||
))
|
||||
}
|
||||
|
||||
@@ -150,6 +181,7 @@ impl CompiledModule {
|
||||
imports: Imports,
|
||||
data_initializers: Box<[OwnedDataInitializer]>,
|
||||
signatures: BoxedSlice<SignatureIndex, VMSharedSignatureIndex>,
|
||||
dbg_jit_registration: Option<GdbJitImageRegistration>,
|
||||
) -> Self {
|
||||
Self {
|
||||
module: Rc::new(module),
|
||||
@@ -158,6 +190,7 @@ impl CompiledModule {
|
||||
imports,
|
||||
data_initializers,
|
||||
signatures,
|
||||
dbg_jit_registration: dbg_jit_registration.map(|r| Rc::new(r)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,6 +215,7 @@ impl CompiledModule {
|
||||
self.imports.clone(),
|
||||
&data_initializers,
|
||||
self.signatures.clone(),
|
||||
self.dbg_jit_registration.as_ref().map(|r| Rc::clone(&r)),
|
||||
Box::new(()),
|
||||
)
|
||||
}
|
||||
@@ -215,8 +249,9 @@ pub fn instantiate(
|
||||
data: &[u8],
|
||||
resolver: &mut dyn Resolver,
|
||||
global_exports: Rc<RefCell<HashMap<String, Option<Export>>>>,
|
||||
debug_info: bool,
|
||||
) -> Result<InstanceHandle, SetupError> {
|
||||
let raw = RawCompiledModule::new(compiler, data, resolver)?;
|
||||
let raw = RawCompiledModule::new(compiler, data, resolver, debug_info)?;
|
||||
|
||||
InstanceHandle::new(
|
||||
Rc::new(raw.module),
|
||||
@@ -225,6 +260,7 @@ pub fn instantiate(
|
||||
raw.imports,
|
||||
&*raw.data_initializers,
|
||||
raw.signatures,
|
||||
raw.dbg_jit_registration.map(|r| Rc::new(r)),
|
||||
Box::new(()),
|
||||
)
|
||||
.map_err(SetupError::Instantiate)
|
||||
|
||||
Reference in New Issue
Block a user