Merge pull request #2811 from peterhuene/improve-store-registration
Refactor store frame information.
This commit is contained in:
@@ -148,6 +148,28 @@ where
|
|||||||
pub fn into_boxed_slice(self) -> BoxedSlice<K, V> {
|
pub fn into_boxed_slice(self) -> BoxedSlice<K, V> {
|
||||||
unsafe { BoxedSlice::<K, V>::from_raw(Box::<[V]>::into_raw(self.elems.into_boxed_slice())) }
|
unsafe { BoxedSlice::<K, V>::from_raw(Box::<[V]>::into_raw(self.elems.into_boxed_slice())) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Performs a binary search on the values with a key extraction function.
|
||||||
|
///
|
||||||
|
/// Assumes that the values are sorted by the key extracted by the function.
|
||||||
|
///
|
||||||
|
/// If the value is found then `Ok(K)` is returned, containing the entity key
|
||||||
|
/// of the matching value.
|
||||||
|
///
|
||||||
|
/// If there are multiple matches, then any one of the matches could be returned.
|
||||||
|
///
|
||||||
|
/// If the value is not found then Err(K) is returned, containing the entity key
|
||||||
|
/// where a matching element could be inserted while maintaining sorted order.
|
||||||
|
pub fn binary_search_values_by_key<'a, B, F>(&'a self, b: &B, f: F) -> Result<K, K>
|
||||||
|
where
|
||||||
|
F: FnMut(&'a V) -> B,
|
||||||
|
B: Ord,
|
||||||
|
{
|
||||||
|
self.elems
|
||||||
|
.binary_search_by_key(b, f)
|
||||||
|
.map(|i| K::new(i))
|
||||||
|
.map_err(|i| K::new(i))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K, V> Default for PrimaryMap<K, V>
|
impl<K, V> Default for PrimaryMap<K, V>
|
||||||
|
|||||||
@@ -61,6 +61,15 @@ impl<'a> CodeMemoryObjectAllocation<'a> {
|
|||||||
pub fn code_range(self) -> &'a mut [u8] {
|
pub fn code_range(self) -> &'a mut [u8] {
|
||||||
self.buf
|
self.buf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn funcs_len(&self) -> usize {
|
||||||
|
self.funcs.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn trampolines_len(&self) -> usize {
|
||||||
|
self.trampolines.len()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn funcs(&'a self) -> impl Iterator<Item = (FuncIndex, &'a mut [VMFunctionBody])> + 'a {
|
pub fn funcs(&'a self) -> impl Iterator<Item = (FuncIndex, &'a mut [VMFunctionBody])> + 'a {
|
||||||
let buf = self.buf as *const _ as *mut [u8];
|
let buf = self.buf as *const _ as *mut [u8];
|
||||||
self.funcs.iter().map(move |(i, (start, len))| {
|
self.funcs.iter().map(move |(i, (start, len))| {
|
||||||
@@ -69,6 +78,7 @@ impl<'a> CodeMemoryObjectAllocation<'a> {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn trampolines(
|
pub fn trampolines(
|
||||||
&'a self,
|
&'a self,
|
||||||
) -> impl Iterator<Item = (SignatureIndex, &'a mut [VMFunctionBody])> + 'a {
|
) -> impl Iterator<Item = (SignatureIndex, &'a mut [VMFunctionBody])> + 'a {
|
||||||
|
|||||||
@@ -195,11 +195,19 @@ pub struct TypeTables {
|
|||||||
|
|
||||||
/// Container for data needed for an Instance function to exist.
|
/// Container for data needed for an Instance function to exist.
|
||||||
pub struct ModuleCode {
|
pub struct ModuleCode {
|
||||||
|
range: (usize, usize),
|
||||||
code_memory: CodeMemory,
|
code_memory: CodeMemory,
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
dbg_jit_registration: Option<GdbJitImageRegistration>,
|
dbg_jit_registration: Option<GdbJitImageRegistration>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ModuleCode {
|
||||||
|
/// Gets the [begin, end) range of the module's code.
|
||||||
|
pub fn range(&self) -> (usize, usize) {
|
||||||
|
self.range
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A compiled wasm module, ready to be instantiated.
|
/// A compiled wasm module, ready to be instantiated.
|
||||||
pub struct CompiledModule {
|
pub struct CompiledModule {
|
||||||
artifacts: CompilationArtifacts,
|
artifacts: CompilationArtifacts,
|
||||||
@@ -259,10 +267,13 @@ impl CompiledModule {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let finished_functions = FinishedFunctions(finished_functions);
|
let finished_functions = FinishedFunctions(finished_functions);
|
||||||
|
let start = code_range.0 as usize;
|
||||||
|
let end = start + code_range.1;
|
||||||
|
|
||||||
Ok(Arc::new(Self {
|
Ok(Arc::new(Self {
|
||||||
artifacts,
|
artifacts,
|
||||||
code: Arc::new(ModuleCode {
|
code: Arc::new(ModuleCode {
|
||||||
|
range: (start, end),
|
||||||
code_memory,
|
code_memory,
|
||||||
dbg_jit_registration,
|
dbg_jit_registration,
|
||||||
}),
|
}),
|
||||||
@@ -312,25 +323,53 @@ impl CompiledModule {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iterates over all functions in this module, returning information about
|
/// Lookups a defined function by a program counter value.
|
||||||
/// how to decode traps which happen in the function.
|
///
|
||||||
pub fn trap_information(
|
/// Returns the defined function index, the start address, and the end address (exclusive).
|
||||||
&self,
|
pub fn func_by_pc(&self, pc: usize) -> Option<(DefinedFuncIndex, usize, usize)> {
|
||||||
) -> impl Iterator<
|
let functions = self.finished_functions();
|
||||||
Item = (
|
|
||||||
DefinedFuncIndex,
|
let index = match functions.binary_search_values_by_key(&pc, |body| unsafe {
|
||||||
*mut [VMFunctionBody],
|
debug_assert!(!(**body).is_empty());
|
||||||
&[TrapInformation],
|
// Return the inclusive "end" of the function
|
||||||
&FunctionAddressMap,
|
(**body).as_ptr() as usize + (**body).len() - 1
|
||||||
),
|
}) {
|
||||||
> {
|
Ok(k) => {
|
||||||
self.finished_functions()
|
// Exact match, pc is at the end of this function
|
||||||
.iter()
|
k
|
||||||
.zip(self.artifacts.funcs.values())
|
}
|
||||||
.map(|((i, alloc), func)| (i, *alloc, func.traps.as_slice(), &func.address_map))
|
Err(k) => {
|
||||||
|
// Not an exact match, k is where `pc` would be "inserted"
|
||||||
|
// Since we key based on the end, function `k` might contain `pc`,
|
||||||
|
// so we'll validate on the range check below
|
||||||
|
k
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let body = functions.get(index)?;
|
||||||
|
let (start, end) = unsafe {
|
||||||
|
let ptr = (**body).as_ptr();
|
||||||
|
let len = (**body).len();
|
||||||
|
(ptr as usize, ptr as usize + len)
|
||||||
|
};
|
||||||
|
|
||||||
|
if pc < start || end < pc {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some((index, start, end))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns all ranges convered by JIT code.
|
/// Gets the function information for a given function index.
|
||||||
|
pub fn func_info(&self, index: DefinedFuncIndex) -> (&FunctionAddressMap, &[TrapInformation]) {
|
||||||
|
self.artifacts
|
||||||
|
.funcs
|
||||||
|
.get(index)
|
||||||
|
.map(|f| (&f.address_map, f.traps.as_ref()))
|
||||||
|
.expect("defined function should be present")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns all ranges covered by JIT code.
|
||||||
pub fn jit_code_ranges<'a>(&'a self) -> impl Iterator<Item = (usize, usize)> + 'a {
|
pub fn jit_code_ranges<'a>(&'a self) -> impl Iterator<Item = (usize, usize)> + 'a {
|
||||||
self.code.code_memory.published_ranges()
|
self.code.code_memory.published_ranges()
|
||||||
}
|
}
|
||||||
@@ -454,25 +493,33 @@ fn build_code_memory(
|
|||||||
|
|
||||||
let allocation = code_memory.allocate_for_object(&obj, unwind_info)?;
|
let allocation = code_memory.allocate_for_object(&obj, unwind_info)?;
|
||||||
|
|
||||||
// Second, create a PrimaryMap from result vector of pointers.
|
// Populate the finished functions from the allocation
|
||||||
let mut finished_functions = PrimaryMap::new();
|
let mut finished_functions = PrimaryMap::with_capacity(allocation.funcs_len());
|
||||||
for (i, fat_ptr) in allocation.funcs() {
|
for (i, fat_ptr) in allocation.funcs() {
|
||||||
|
let start = fat_ptr.as_ptr() as usize;
|
||||||
let fat_ptr: *mut [VMFunctionBody] = fat_ptr;
|
let fat_ptr: *mut [VMFunctionBody] = fat_ptr;
|
||||||
|
// Assert that the function bodies are pushed in sort order
|
||||||
|
// This property is relied upon to search for functions by PC values
|
||||||
|
assert!(
|
||||||
|
start
|
||||||
|
> finished_functions
|
||||||
|
.last()
|
||||||
|
.map(|f: &*mut [VMFunctionBody]| unsafe { (**f).as_ptr() as usize })
|
||||||
|
.unwrap_or(0)
|
||||||
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some(finished_functions.push(fat_ptr)),
|
Some(finished_functions.push(fat_ptr)),
|
||||||
module.defined_func_index(i)
|
module.defined_func_index(i)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let trampolines = allocation
|
// Populate the trampolines from the allocation
|
||||||
.trampolines()
|
let mut trampolines = Vec::with_capacity(allocation.trampolines_len());
|
||||||
.map(|(i, fat_ptr)| {
|
for (i, fat_ptr) in allocation.trampolines() {
|
||||||
let fnptr = unsafe {
|
let fnptr =
|
||||||
std::mem::transmute::<*const VMFunctionBody, VMTrampoline>(fat_ptr.as_ptr())
|
unsafe { std::mem::transmute::<*const VMFunctionBody, VMTrampoline>(fat_ptr.as_ptr()) };
|
||||||
};
|
trampolines.push((i, fnptr));
|
||||||
(i, fnptr)
|
}
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let code_range = allocation.code_range();
|
let code_range = allocation.code_range();
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,14 @@
|
|||||||
use std::cmp;
|
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use wasmtime_environ::entity::EntityRef;
|
use wasmtime_environ::entity::EntityRef;
|
||||||
use wasmtime_environ::ir;
|
use wasmtime_environ::ir;
|
||||||
use wasmtime_environ::wasm::FuncIndex;
|
use wasmtime_environ::wasm::DefinedFuncIndex;
|
||||||
use wasmtime_environ::{FunctionAddressMap, Module, TrapInformation};
|
use wasmtime_environ::{FunctionAddressMap, TrapInformation};
|
||||||
use wasmtime_jit::{CompiledModule, SymbolizeContext};
|
use wasmtime_jit::CompiledModule;
|
||||||
|
|
||||||
/// This is a structure that lives within a `Store` and retains information
|
/// This is a structure that lives within a `Store` and retains information
|
||||||
/// about all wasm code registered with the `Store` (e.g. modules that have
|
/// about all modules registered with the `Store` via instantiation.
|
||||||
/// been instantiated into a store).
|
|
||||||
///
|
///
|
||||||
/// "frame information" here refers to things like determining whether a
|
/// "frame information" here refers to things like determining whether a
|
||||||
/// program counter is a wasm program counter, and additionally mapping program
|
/// program counter is a wasm program counter, and additionally mapping program
|
||||||
@@ -31,25 +29,6 @@ pub struct StoreFrameInfo {
|
|||||||
ranges: BTreeMap<usize, ModuleFrameInfo>,
|
ranges: BTreeMap<usize, ModuleFrameInfo>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This is a listing of information for each module registered with a store
|
|
||||||
/// which lives in `StoreFrameInfo`.
|
|
||||||
struct ModuleFrameInfo {
|
|
||||||
start: usize,
|
|
||||||
functions: Arc<BTreeMap<usize, FunctionInfo>>,
|
|
||||||
module: Arc<Module>,
|
|
||||||
symbolize: Option<SymbolizeContext>,
|
|
||||||
has_unparsed_debuginfo: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Information about a function, specifically information about individual
|
|
||||||
/// traps and such.
|
|
||||||
struct FunctionInfo {
|
|
||||||
start: usize,
|
|
||||||
index: FuncIndex,
|
|
||||||
traps: Vec<TrapInformation>,
|
|
||||||
instr_map: FunctionAddressMap,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StoreFrameInfo {
|
impl StoreFrameInfo {
|
||||||
/// Fetches frame information about a program counter in a backtrace.
|
/// Fetches frame information about a program counter in a backtrace.
|
||||||
///
|
///
|
||||||
@@ -58,8 +37,103 @@ impl StoreFrameInfo {
|
|||||||
/// returned indicates whether the original module has unparsed debug
|
/// returned indicates whether the original module has unparsed debug
|
||||||
/// information due to the compiler's configuration.
|
/// information due to the compiler's configuration.
|
||||||
pub fn lookup_frame_info(&self, pc: usize) -> Option<(FrameInfo, bool)> {
|
pub fn lookup_frame_info(&self, pc: usize) -> Option<(FrameInfo, bool)> {
|
||||||
let (module, func) = self.func(pc)?;
|
let module = self.module(pc)?;
|
||||||
let pos = func.instr_pos(pc);
|
module
|
||||||
|
.lookup_frame_info(pc)
|
||||||
|
.map(|info| (info, module.has_unparsed_debuginfo()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns whether the `pc` specified is contained within some module's
|
||||||
|
/// function.
|
||||||
|
pub fn contains_pc(&self, pc: usize) -> bool {
|
||||||
|
match self.module(pc) {
|
||||||
|
Some(module) => module.contains_pc(pc),
|
||||||
|
None => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fetches trap information about a program counter in a backtrace.
|
||||||
|
pub fn lookup_trap_info(&self, pc: usize) -> Option<&TrapInformation> {
|
||||||
|
self.module(pc)?.lookup_trap_info(pc)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn module(&self, pc: usize) -> Option<&ModuleFrameInfo> {
|
||||||
|
let (end, info) = self.ranges.range(pc..).next()?;
|
||||||
|
if pc < info.start || *end < pc {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(info)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Registers a new compiled module's frame information.
|
||||||
|
pub fn register(&mut self, module: &Arc<CompiledModule>) {
|
||||||
|
let (start, end) = module.code().range();
|
||||||
|
|
||||||
|
// Ignore modules with no code or finished functions
|
||||||
|
if start == end || module.finished_functions().is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The module code range is exclusive for end, so make it inclusive as it
|
||||||
|
// may be a valid PC value
|
||||||
|
let end = end - 1;
|
||||||
|
|
||||||
|
if self.contains_pc(start) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assert that this module's code doesn't collide with any other registered modules
|
||||||
|
if let Some((_, prev)) = self.ranges.range(end..).next() {
|
||||||
|
assert!(prev.start > end);
|
||||||
|
}
|
||||||
|
if let Some((prev_end, _)) = self.ranges.range(..=start).next_back() {
|
||||||
|
assert!(*prev_end < start);
|
||||||
|
}
|
||||||
|
|
||||||
|
let prev = self.ranges.insert(
|
||||||
|
end,
|
||||||
|
ModuleFrameInfo {
|
||||||
|
start,
|
||||||
|
module: module.clone(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
assert!(prev.is_none());
|
||||||
|
|
||||||
|
GLOBAL_INFO.lock().unwrap().register(start, end, module);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for StoreFrameInfo {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let mut info = GLOBAL_INFO.lock().unwrap();
|
||||||
|
for end in self.ranges.keys() {
|
||||||
|
info.unregister(*end);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents a module's frame information.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct ModuleFrameInfo {
|
||||||
|
start: usize,
|
||||||
|
module: Arc<CompiledModule>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ModuleFrameInfo {
|
||||||
|
/// Determines if the related module has unparsed debug information.
|
||||||
|
pub fn has_unparsed_debuginfo(&self) -> bool {
|
||||||
|
self.module.has_unparsed_debuginfo()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fetches frame information about a program counter in a backtrace.
|
||||||
|
///
|
||||||
|
/// Returns an object if this `pc` is known to this module, or returns `None`
|
||||||
|
/// if no information can be found.
|
||||||
|
pub fn lookup_frame_info(&self, pc: usize) -> Option<FrameInfo> {
|
||||||
|
let (index, offset) = self.func(pc)?;
|
||||||
|
let (addr_map, _) = self.module.func_info(index);
|
||||||
|
let pos = Self::instr_pos(offset, addr_map);
|
||||||
|
|
||||||
// In debug mode for now assert that we found a mapping for `pc` within
|
// In debug mode for now assert that we found a mapping for `pc` within
|
||||||
// the function, because otherwise something is buggy along the way and
|
// the function, because otherwise something is buggy along the way and
|
||||||
@@ -68,8 +142,8 @@ impl StoreFrameInfo {
|
|||||||
debug_assert!(pos.is_some(), "failed to find instruction for {:x}", pc);
|
debug_assert!(pos.is_some(), "failed to find instruction for {:x}", pc);
|
||||||
|
|
||||||
let instr = match pos {
|
let instr = match pos {
|
||||||
Some(pos) => func.instr_map.instructions[pos].srcloc,
|
Some(pos) => addr_map.instructions[pos].srcloc,
|
||||||
None => func.instr_map.start_srcloc,
|
None => addr_map.start_srcloc,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Use our wasm-relative pc to symbolize this frame. If there's a
|
// Use our wasm-relative pc to symbolize this frame. If there's a
|
||||||
@@ -81,7 +155,8 @@ impl StoreFrameInfo {
|
|||||||
// here for now since technically wasm modules can always have any
|
// here for now since technically wasm modules can always have any
|
||||||
// custom section contents.
|
// custom section contents.
|
||||||
let mut symbols = Vec::new();
|
let mut symbols = Vec::new();
|
||||||
if let Some(s) = &module.symbolize {
|
|
||||||
|
if let Some(s) = &self.module.symbolize_context().ok().and_then(|c| c) {
|
||||||
let to_lookup = (instr.bits() as u64) - s.code_section_offset();
|
let to_lookup = (instr.bits() as u64) - s.code_section_offset();
|
||||||
if let Ok(mut frames) = s.addr2line().find_frames(to_lookup) {
|
if let Ok(mut frames) = s.addr2line().find_frames(to_lookup) {
|
||||||
while let Ok(Some(frame)) = frames.next() {
|
while let Ok(Some(frame)) = frames.next() {
|
||||||
@@ -103,20 +178,20 @@ impl StoreFrameInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Some((
|
let module = self.module.module();
|
||||||
FrameInfo {
|
let index = module.func_index(index);
|
||||||
module_name: module.module.name.clone(),
|
|
||||||
func_index: func.index.index() as u32,
|
Some(FrameInfo {
|
||||||
func_name: module.module.func_names.get(&func.index).cloned(),
|
module_name: module.name.clone(),
|
||||||
instr,
|
func_index: index.index() as u32,
|
||||||
func_start: func.instr_map.start_srcloc,
|
func_name: module.func_names.get(&index).cloned(),
|
||||||
symbols,
|
instr,
|
||||||
},
|
func_start: addr_map.start_srcloc,
|
||||||
module.has_unparsed_debuginfo,
|
symbols,
|
||||||
))
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns whether the `pc` specified is contaained within some module's
|
/// Returns whether the `pc` specified is contained within some module's
|
||||||
/// function.
|
/// function.
|
||||||
pub fn contains_pc(&self, pc: usize) -> bool {
|
pub fn contains_pc(&self, pc: usize) -> bool {
|
||||||
self.func(pc).is_some()
|
self.func(pc).is_some()
|
||||||
@@ -124,89 +199,26 @@ impl StoreFrameInfo {
|
|||||||
|
|
||||||
/// Fetches trap information about a program counter in a backtrace.
|
/// Fetches trap information about a program counter in a backtrace.
|
||||||
pub fn lookup_trap_info(&self, pc: usize) -> Option<&TrapInformation> {
|
pub fn lookup_trap_info(&self, pc: usize) -> Option<&TrapInformation> {
|
||||||
let (_module, func) = self.func(pc)?;
|
let (index, offset) = self.func(pc)?;
|
||||||
let idx = func
|
let (_, traps) = self.module.func_info(index);
|
||||||
.traps
|
let idx = traps
|
||||||
.binary_search_by_key(&((pc - func.start) as u32), |info| info.code_offset)
|
.binary_search_by_key(&offset, |info| info.code_offset)
|
||||||
.ok()?;
|
.ok()?;
|
||||||
Some(&func.traps[idx])
|
Some(&traps[idx])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn func(&self, pc: usize) -> Option<(&ModuleFrameInfo, &FunctionInfo)> {
|
fn func(&self, pc: usize) -> Option<(DefinedFuncIndex, u32)> {
|
||||||
func(pc, &self.ranges, |t| (t.start, &t.functions))
|
let (index, start, _) = self.module.func_by_pc(pc)?;
|
||||||
|
Some((index, (pc - start) as u32))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Registers a new compiled module's frame information.
|
fn instr_pos(offset: u32, addr_map: &FunctionAddressMap) -> Option<usize> {
|
||||||
///
|
|
||||||
/// This function will register the `names` information for all of the
|
|
||||||
/// compiled functions within `module`. If the `module` has no functions
|
|
||||||
/// then `None` will be returned. Otherwise the returned object, when
|
|
||||||
/// dropped, will be used to unregister all name information from this map.
|
|
||||||
pub fn register(&mut self, module: &CompiledModule) {
|
|
||||||
let mut min = usize::max_value();
|
|
||||||
let mut max = 0;
|
|
||||||
let mut functions = BTreeMap::new();
|
|
||||||
for (i, allocated, traps, address_map) in module.trap_information() {
|
|
||||||
let (start, end) = unsafe {
|
|
||||||
let ptr = (*allocated).as_ptr();
|
|
||||||
let len = (*allocated).len();
|
|
||||||
// First and last byte of the function text.
|
|
||||||
(ptr as usize, ptr as usize + len - 1)
|
|
||||||
};
|
|
||||||
// Skip empty functions.
|
|
||||||
if end < start {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
min = cmp::min(min, start);
|
|
||||||
max = cmp::max(max, end);
|
|
||||||
let func = FunctionInfo {
|
|
||||||
start,
|
|
||||||
index: module.module().func_index(i),
|
|
||||||
traps: traps.to_vec(),
|
|
||||||
instr_map: address_map.clone(),
|
|
||||||
};
|
|
||||||
assert!(functions.insert(end, func).is_none());
|
|
||||||
}
|
|
||||||
if functions.len() == 0 {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let functions = Arc::new(functions);
|
|
||||||
|
|
||||||
// First up assert that our chunk of jit functions doesn't collide with
|
|
||||||
// any other known chunks of jit functions...
|
|
||||||
if let Some((_, prev)) = self.ranges.range(max..).next() {
|
|
||||||
assert!(prev.start > max);
|
|
||||||
}
|
|
||||||
if let Some((prev_end, _)) = self.ranges.range(..=min).next_back() {
|
|
||||||
assert!(*prev_end < min);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ... then insert our range and assert nothing was there previously
|
|
||||||
GLOBAL_INFO.lock().unwrap().register(min, max, &functions);
|
|
||||||
let prev = self.ranges.insert(
|
|
||||||
max,
|
|
||||||
ModuleFrameInfo {
|
|
||||||
start: min,
|
|
||||||
functions,
|
|
||||||
module: module.module().clone(),
|
|
||||||
symbolize: module.symbolize_context().ok().and_then(|c| c),
|
|
||||||
has_unparsed_debuginfo: module.has_unparsed_debuginfo(),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
assert!(prev.is_none());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FunctionInfo {
|
|
||||||
fn instr_pos(&self, pc: usize) -> Option<usize> {
|
|
||||||
// Use our relative position from the start of the function to find the
|
// Use our relative position from the start of the function to find the
|
||||||
// machine instruction that corresponds to `pc`, which then allows us to
|
// machine instruction that corresponds to `pc`, which then allows us to
|
||||||
// map that to a wasm original source location.
|
// map that to a wasm original source location.
|
||||||
let rel_pos = (pc - self.start) as u32;
|
match addr_map
|
||||||
match self
|
|
||||||
.instr_map
|
|
||||||
.instructions
|
.instructions
|
||||||
.binary_search_by_key(&rel_pos, |map| map.code_offset)
|
.binary_search_by_key(&offset, |map| map.code_offset)
|
||||||
{
|
{
|
||||||
// Exact hit!
|
// Exact hit!
|
||||||
Ok(pos) => Some(pos),
|
Ok(pos) => Some(pos),
|
||||||
@@ -221,15 +233,6 @@ impl FunctionInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for StoreFrameInfo {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
let mut info = GLOBAL_INFO.lock().unwrap();
|
|
||||||
for end in self.ranges.keys() {
|
|
||||||
info.unregister(*end);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This is the dual of `StoreFrameInfo` and is stored globally (as the name
|
/// This is the dual of `StoreFrameInfo` and is stored globally (as the name
|
||||||
/// implies) rather than simply in one `Store`.
|
/// implies) rather than simply in one `Store`.
|
||||||
///
|
///
|
||||||
@@ -244,16 +247,14 @@ impl Drop for StoreFrameInfo {
|
|||||||
/// information. When a `StoreFrameInfo` is destroyed then all of its entries
|
/// information. When a `StoreFrameInfo` is destroyed then all of its entries
|
||||||
/// are removed from the global frame information.
|
/// are removed from the global frame information.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub(crate) struct GlobalFrameInfo {
|
pub struct GlobalFrameInfo {
|
||||||
// The map here behaves the same way as `StoreFrameInfo`.
|
// The map here behaves the same way as `StoreFrameInfo`.
|
||||||
ranges: BTreeMap<usize, GlobalModuleFrameInfo>,
|
ranges: BTreeMap<usize, GlobalModuleFrameInfo>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This is the equivalent of `ModuleFrameInfo` except has less code and is
|
/// This is the equivalent of `ModuleFrameInfo` except it keeps a reference count.
|
||||||
/// stored within `GlobalFrameInfo`.
|
|
||||||
struct GlobalModuleFrameInfo {
|
struct GlobalModuleFrameInfo {
|
||||||
start: usize,
|
module: ModuleFrameInfo,
|
||||||
functions: Arc<BTreeMap<usize, FunctionInfo>>,
|
|
||||||
|
|
||||||
/// Note that modules can be instantiated in many stores, so the purpose of
|
/// Note that modules can be instantiated in many stores, so the purpose of
|
||||||
/// this field is to keep track of how many stores have registered a
|
/// this field is to keep track of how many stores have registered a
|
||||||
@@ -271,64 +272,57 @@ impl GlobalFrameInfo {
|
|||||||
/// is a wasm trap or not.
|
/// is a wasm trap or not.
|
||||||
pub(crate) fn is_wasm_pc(pc: usize) -> bool {
|
pub(crate) fn is_wasm_pc(pc: usize) -> bool {
|
||||||
let info = GLOBAL_INFO.lock().unwrap();
|
let info = GLOBAL_INFO.lock().unwrap();
|
||||||
match func(pc, &info.ranges, |i| (i.start, &i.functions)) {
|
|
||||||
Some((_, info)) => info.instr_pos(pc).is_some(),
|
match info.ranges.range(pc..).next() {
|
||||||
|
Some((end, info)) => {
|
||||||
|
if pc < info.module.start || *end < pc {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
match info.module.func(pc) {
|
||||||
|
Some((index, offset)) => {
|
||||||
|
let (addr_map, _) = info.module.module.func_info(index);
|
||||||
|
ModuleFrameInfo::instr_pos(offset, addr_map).is_some()
|
||||||
|
}
|
||||||
|
None => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
None => false,
|
None => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Registers a new region of code, described by `(start, end)` and with
|
/// Registers a new region of code, described by `(start, end)` and with
|
||||||
/// the given function information, with the global information.
|
/// the given function information, with the global information.
|
||||||
fn register(
|
fn register(&mut self, start: usize, end: usize, module: &Arc<CompiledModule>) {
|
||||||
&mut self,
|
|
||||||
start: usize,
|
|
||||||
end: usize,
|
|
||||||
functions: &Arc<BTreeMap<usize, FunctionInfo>>,
|
|
||||||
) {
|
|
||||||
let info = self
|
let info = self
|
||||||
.ranges
|
.ranges
|
||||||
.entry(end)
|
.entry(end)
|
||||||
.or_insert_with(|| GlobalModuleFrameInfo {
|
.or_insert_with(|| GlobalModuleFrameInfo {
|
||||||
start,
|
module: ModuleFrameInfo {
|
||||||
functions: functions.clone(),
|
start,
|
||||||
|
module: module.clone(),
|
||||||
|
},
|
||||||
references: 0,
|
references: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Note that ideally we'd debug_assert that the information previously
|
// Note that ideally we'd debug_assert that the information previously
|
||||||
// stored, if any, matches the `functions` we were given, but for now we
|
// stored, if any, matches the `functions` we were given, but for now we
|
||||||
// just do some simple checks to hope it's the same.
|
// just do some simple checks to hope it's the same.
|
||||||
assert_eq!(info.start, start);
|
assert_eq!(info.module.start, start);
|
||||||
assert_eq!(info.functions.len(), functions.len());
|
|
||||||
info.references += 1;
|
info.references += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Unregisters a region of code (keyed by the `end` address) from this
|
/// Unregisters a region of code (keyed by the `end` address) from this
|
||||||
/// global information.
|
/// global information.
|
||||||
fn unregister(&mut self, end: usize) {
|
fn unregister(&mut self, end: usize) {
|
||||||
let val = self.ranges.get_mut(&end).unwrap();
|
let info = self.ranges.get_mut(&end).unwrap();
|
||||||
val.references -= 1;
|
info.references -= 1;
|
||||||
if val.references == 0 {
|
if info.references == 0 {
|
||||||
self.ranges.remove(&end);
|
self.ranges.remove(&end);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn func<T>(
|
|
||||||
pc: usize,
|
|
||||||
ranges: &BTreeMap<usize, T>,
|
|
||||||
get_start_and_functions: impl FnOnce(&T) -> (usize, &BTreeMap<usize, FunctionInfo>),
|
|
||||||
) -> Option<(&T, &FunctionInfo)> {
|
|
||||||
let (end, info) = ranges.range(pc..).next()?;
|
|
||||||
let (start, functions) = get_start_and_functions(info);
|
|
||||||
if pc < start || *end < pc {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let (end, func) = functions.range(pc..).next()?;
|
|
||||||
if pc < func.start || *end < pc {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
Some((info, func))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Description of a frame in a backtrace for a [`Trap`].
|
/// Description of a frame in a backtrace for a [`Trap`].
|
||||||
///
|
///
|
||||||
/// Whenever a WebAssembly trap occurs an instance of [`Trap`] is created. Each
|
/// Whenever a WebAssembly trap occurs an instance of [`Trap`] is created. Each
|
||||||
|
|||||||
@@ -436,7 +436,7 @@ impl Module {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn compiled_module(&self) -> &CompiledModule {
|
pub(crate) fn compiled_module(&self) -> &Arc<CompiledModule> {
|
||||||
&self.inner.module
|
&self.inner.module
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -294,17 +294,8 @@ impl Store {
|
|||||||
.insert(ArcModuleCode(module.compiled_module().code().clone()));
|
.insert(ArcModuleCode(module.compiled_module().code().clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn register_jit_code(&self, module: &CompiledModule) {
|
fn register_jit_code(&self, module: &Arc<CompiledModule>) {
|
||||||
let functions = module.finished_functions();
|
self.inner.frame_info.borrow_mut().register(module)
|
||||||
let first_pc = match functions.values().next() {
|
|
||||||
Some(f) => unsafe { (**f).as_ptr() as usize },
|
|
||||||
None => return,
|
|
||||||
};
|
|
||||||
// Only register this module if it hasn't already been registered.
|
|
||||||
let mut info = self.inner.frame_info.borrow_mut();
|
|
||||||
if !info.contains_pc(first_pc) {
|
|
||||||
info.register(module);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn register_stack_maps(&self, module: &CompiledModule) {
|
fn register_stack_maps(&self, module: &CompiledModule) {
|
||||||
|
|||||||
Reference in New Issue
Block a user