Backtrace WebAssembly function JIT frames (#759)

* Create backtrace

* Extend unwind information with FDE data.

* Expose backtrace via API/Trap

* wasmtime_call returns not-str

* Return Arc<JITFrameTag>

* rename frame -> function

* Fix windows crashes and unwrap UNWIND_HISTORY_TABLE

* mmaps -> entries

* pass a backtrace in ActionOutcome

* add test_trap_stack_overflow

* Update cranelift version.
This commit is contained in:
Yury Delendik
2020-01-15 13:48:24 -06:00
committed by GitHub
parent 0848a7eaaa
commit 2a50701f0a
26 changed files with 803 additions and 149 deletions

View File

@@ -2,14 +2,45 @@
use crate::function_table::FunctionTable;
use region;
use std::mem::ManuallyDrop;
use std::{cmp, mem};
use wasmtime_environ::{Compilation, CompiledFunction};
use wasmtime_runtime::{Mmap, VMFunctionBody};
struct CodeMemoryEntry {
mmap: ManuallyDrop<Mmap>,
table: ManuallyDrop<FunctionTable>,
}
impl CodeMemoryEntry {
fn new() -> Self {
Self {
mmap: ManuallyDrop::new(Mmap::new()),
table: ManuallyDrop::new(FunctionTable::new()),
}
}
fn with_capacity(cap: usize) -> Result<Self, String> {
Ok(Self {
mmap: ManuallyDrop::new(Mmap::with_at_least(cap)?),
table: ManuallyDrop::new(FunctionTable::new()),
})
}
}
impl Drop for CodeMemoryEntry {
fn drop(&mut self) {
unsafe {
// Table needs to be freed before mmap.
ManuallyDrop::drop(&mut self.table);
ManuallyDrop::drop(&mut self.mmap);
}
}
}
/// Memory manager for executable code.
pub struct CodeMemory {
current: (Mmap, FunctionTable),
mmaps: Vec<(Mmap, FunctionTable)>,
current: CodeMemoryEntry,
entries: Vec<CodeMemoryEntry>,
position: usize,
published: usize,
}
@@ -23,8 +54,8 @@ impl CodeMemory {
/// Create a new `CodeMemory` instance.
pub fn new() -> Self {
Self {
current: (Mmap::new(), FunctionTable::new()),
mmaps: Vec::new(),
current: CodeMemoryEntry::new(),
entries: Vec::new(),
position: 0,
published: 0,
}
@@ -81,19 +112,20 @@ impl CodeMemory {
self.push_current(0)
.expect("failed to push current memory map");
for (m, t) in &mut self.mmaps[self.published..] {
for CodeMemoryEntry { mmap: m, table: t } in &mut self.entries[self.published..] {
// Remove write access to the pages due to the relocation fixups.
t.publish(m.as_ptr() as u64)
.expect("failed to publish function table");
if !m.is_empty() {
unsafe {
region::protect(m.as_mut_ptr(), m.len(), region::Protection::ReadExecute)
}
.expect("unable to make memory readonly and executable");
}
t.publish(m.as_ptr() as u64)
.expect("failed to publish function table");
}
self.published = self.mmaps.len();
self.published = self.entries.len();
}
/// Allocate `size` bytes of memory which can be made executable later by
@@ -103,7 +135,7 @@ impl CodeMemory {
///
/// TODO: Add an alignment flag.
fn allocate(&mut self, size: usize) -> Result<(&mut [u8], &mut FunctionTable), String> {
if self.current.0.len() - self.position < size {
if self.current.mmap.len() - self.position < size {
self.push_current(cmp::max(0x10000, size))?;
}
@@ -111,8 +143,8 @@ impl CodeMemory {
self.position += size;
Ok((
&mut self.current.0.as_mut_slice()[old_position..self.position],
&mut self.current.1,
&mut self.current.mmap.as_mut_slice()[old_position..self.position],
&mut self.current.table,
))
}
@@ -153,12 +185,19 @@ impl CodeMemory {
// Keep unwind information 32-bit aligned (round up to the nearest 4 byte boundary)
let padding = ((func.body.len() + 3) & !3) - func.body.len();
let (unwind, remainder) = remainder.split_at_mut(padding + func.unwind_info.len());
unwind[padding..].copy_from_slice(&func.unwind_info);
let mut relocs = Vec::new();
func.unwind_info
.serialize(&mut unwind[padding..], &mut relocs);
let unwind_start = func_end + (padding as u32);
let unwind_end = unwind_start + (func.unwind_info.len() as u32);
table.add_function(func_start, func_end, unwind_start);
relocs.iter_mut().for_each(move |r| {
r.offset += unwind_start;
r.addend += func_start;
});
table.add_function(func_start, func_end, unwind_start, &relocs);
(unwind_end, remainder, table, vmfunc)
}
@@ -174,20 +213,17 @@ impl CodeMemory {
fn push_current(&mut self, new_size: usize) -> Result<(), String> {
let previous = mem::replace(
&mut self.current,
(
if new_size == 0 {
Mmap::new()
} else {
Mmap::with_at_least(cmp::max(0x10000, new_size))?
},
FunctionTable::new(),
),
if new_size == 0 {
CodeMemoryEntry::new()
} else {
CodeMemoryEntry::with_capacity(cmp::max(0x10000, new_size))?
},
);
if !previous.0.is_empty() {
self.mmaps.push(previous);
if !previous.mmap.is_empty() {
self.entries.push(previous);
} else {
assert_eq!(previous.1.len(), 0);
assert_eq!(previous.table.len(), 0);
}
self.position = 0;