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:
Yury Delendik
2019-03-06 18:03:32 -06:00
committed by Dan Gohman
parent 6eb09d9edd
commit ddbc00752e
36 changed files with 2252 additions and 85 deletions

View File

@@ -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 }

View File

@@ -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

View File

@@ -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)
}

View File

@@ -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,
)
}

View File

@@ -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)