Add a compilation section to disable address maps (#3598)
* Add a compilation section to disable address maps This commit adds a new `Config::generate_address_map` compilation setting which is used to disable emission of the `.wasmtime.addrmap` section of compiled artifacts. This section is currently around the size of the entire `.text` section itself unfortunately and for size reasons may wish to be omitted. Functionality-wise all that is lost is knowing the precise wasm module offset address of a faulting instruction or in a backtrace of instructions. This also means that if the module has DWARF debugging information available with it Wasmtime isn't able to produce a filename and line number in the backtrace. This option remains enabled by default. This option may not be needed in the future with #3547 perhaps, but in the meantime it seems reasonable enough to support a configuration mode where the section is entirely omitted if the smallest module possible is desired. * Fix some CI issues * Update tests/all/traps.rs Co-authored-by: Nick Fitzgerald <fitzgen@gmail.com> * Do less work in compilation for address maps But only when disabled Co-authored-by: Nick Fitzgerald <fitzgen@gmail.com>
This commit is contained in:
@@ -1052,6 +1052,20 @@ impl Config {
|
||||
self
|
||||
}
|
||||
|
||||
/// Configures whether compiled artifacts will contain information to map
|
||||
/// native program addresses back to the original wasm module.
|
||||
///
|
||||
/// This configuration option is `true` by default and, if enables,
|
||||
/// generates the appropriate tables in compiled modules to map from native
|
||||
/// address back to wasm source addresses. This is used for displaying wasm
|
||||
/// program counters in backtraces as well as generating filenames/line
|
||||
/// numbers if so configured as well (and the original wasm module has DWARF
|
||||
/// debugging information present).
|
||||
pub fn generate_address_map(&mut self, generate: bool) -> &mut Self {
|
||||
self.tunables.generate_address_map = generate;
|
||||
self
|
||||
}
|
||||
|
||||
pub(crate) fn build_allocator(&self) -> Result<Box<dyn InstanceAllocator>> {
|
||||
#[cfg(feature = "async")]
|
||||
let stack_size = self.async_stack_size;
|
||||
|
||||
@@ -50,7 +50,7 @@ impl Engine {
|
||||
// Ensure that wasmtime_runtime's signal handlers are configured. This
|
||||
// is the per-program initialization required for handling traps, such
|
||||
// as configuring signals, vectored exception handlers, etc.
|
||||
wasmtime_runtime::init_traps(crate::module::GlobalModuleRegistry::is_wasm_pc);
|
||||
wasmtime_runtime::init_traps(crate::module::GlobalModuleRegistry::is_wasm_trap_pc);
|
||||
debug_builtins::ensure_exported();
|
||||
|
||||
let registry = SignatureRegistry::new();
|
||||
|
||||
@@ -398,13 +398,10 @@ impl Module {
|
||||
.collect();
|
||||
|
||||
let mut obj = engine.compiler().object()?;
|
||||
let (funcs, trampolines) = engine.compiler().emit_obj(
|
||||
&translation,
|
||||
&types,
|
||||
funcs,
|
||||
tunables.generate_native_debuginfo,
|
||||
&mut obj,
|
||||
)?;
|
||||
let (funcs, trampolines) =
|
||||
engine
|
||||
.compiler()
|
||||
.emit_obj(&translation, &types, funcs, tunables, &mut obj)?;
|
||||
|
||||
// If configured, attempt to use paged memory initialization
|
||||
// instead of the default mode of memory initialization
|
||||
|
||||
@@ -172,13 +172,12 @@ pub struct GlobalModuleRegistry(BTreeMap<usize, GlobalRegisteredModule>);
|
||||
impl GlobalModuleRegistry {
|
||||
/// Returns whether the `pc`, according to globally registered information,
|
||||
/// is a wasm trap or not.
|
||||
pub(crate) fn is_wasm_pc(pc: usize) -> bool {
|
||||
pub(crate) fn is_wasm_trap_pc(pc: usize) -> bool {
|
||||
let modules = GLOBAL_MODULES.read().unwrap();
|
||||
|
||||
match modules.module(pc) {
|
||||
Some((entry, text_offset)) => {
|
||||
wasmtime_environ::lookup_file_pos(entry.module.address_map_data(), text_offset)
|
||||
.is_some()
|
||||
wasmtime_environ::lookup_trap_code(entry.module.trap_data(), text_offset).is_some()
|
||||
}
|
||||
None => false,
|
||||
}
|
||||
@@ -275,14 +274,15 @@ impl GlobalRegisteredModule {
|
||||
// the function, because otherwise something is buggy along the way and
|
||||
// not accounting for all the instructions. This isn't super critical
|
||||
// though so we can omit this check in release mode.
|
||||
//
|
||||
// Note that if the module doesn't even have an address map due to
|
||||
// compilation settings then it's expected that `instr` is `None`.
|
||||
debug_assert!(
|
||||
instr.is_some(),
|
||||
instr.is_some() || !self.module.has_address_map(),
|
||||
"failed to find instruction for {:#x}",
|
||||
text_offset
|
||||
);
|
||||
|
||||
let instr = instr.unwrap_or(info.start_srcloc);
|
||||
|
||||
// Use our wasm-relative pc to symbolize this frame. If there's a
|
||||
// symbolication context (dwarf debug info) available then we can try to
|
||||
// look this up there.
|
||||
@@ -294,7 +294,7 @@ impl GlobalRegisteredModule {
|
||||
let mut symbols = Vec::new();
|
||||
|
||||
if let Some(s) = &self.module.symbolize_context().ok().and_then(|c| c) {
|
||||
if let Some(offset) = instr.file_offset() {
|
||||
if let Some(offset) = instr.and_then(|i| i.file_offset()) {
|
||||
let to_lookup = u64::from(offset) - s.code_section_offset();
|
||||
if let Ok(mut frames) = s.addr2line().find_frames(to_lookup) {
|
||||
while let Ok(Some(frame)) = frames.next() {
|
||||
@@ -344,7 +344,7 @@ pub struct FrameInfo {
|
||||
func_index: u32,
|
||||
func_name: Option<String>,
|
||||
func_start: FilePos,
|
||||
instr: FilePos,
|
||||
instr: Option<FilePos>,
|
||||
symbols: Vec<FrameSymbol>,
|
||||
}
|
||||
|
||||
@@ -393,8 +393,14 @@ impl FrameInfo {
|
||||
///
|
||||
/// The offset here is the offset from the beginning of the original wasm
|
||||
/// module to the instruction that this frame points to.
|
||||
pub fn module_offset(&self) -> usize {
|
||||
self.instr.file_offset().unwrap_or(u32::MAX) as usize
|
||||
///
|
||||
/// Note that `None` may be returned if the original module was not
|
||||
/// compiled with mapping information to yield this information. This is
|
||||
/// controlled by the
|
||||
/// [`Config::generate_address_map`](crate::Config::generate_address_map)
|
||||
/// configuration option.
|
||||
pub fn module_offset(&self) -> Option<usize> {
|
||||
Some(self.instr?.file_offset()? as usize)
|
||||
}
|
||||
|
||||
/// Returns the offset from the original wasm module's function to this
|
||||
@@ -403,11 +409,15 @@ impl FrameInfo {
|
||||
/// The offset here is the offset from the beginning of the defining
|
||||
/// function of this frame (within the wasm module) to the instruction this
|
||||
/// frame points to.
|
||||
pub fn func_offset(&self) -> usize {
|
||||
match self.instr.file_offset() {
|
||||
Some(i) => (i - self.func_start.file_offset().unwrap()) as usize,
|
||||
None => u32::MAX as usize,
|
||||
}
|
||||
///
|
||||
/// Note that `None` may be returned if the original module was not
|
||||
/// compiled with mapping information to yield this information. This is
|
||||
/// controlled by the
|
||||
/// [`Config::generate_address_map`](crate::Config::generate_address_map)
|
||||
/// configuration option.
|
||||
pub fn func_offset(&self) -> Option<usize> {
|
||||
let instr_offset = self.instr?.file_offset()?;
|
||||
Some((instr_offset - self.func_start.file_offset()?) as usize)
|
||||
}
|
||||
|
||||
/// Returns the debug symbols found, if any, for this function frame.
|
||||
|
||||
@@ -601,6 +601,12 @@ impl<'a> SerializedModule<'a> {
|
||||
|
||||
// This doesn't affect compilation, it's just a runtime setting.
|
||||
dynamic_memory_growth_reserve: _,
|
||||
|
||||
// This does technically affect compilation but modules with/without
|
||||
// trap information can be loaded into engines with the opposite
|
||||
// setting just fine (it's just a section in the compiled file and
|
||||
// whether it's present or not)
|
||||
generate_address_map: _,
|
||||
} = self.metadata.tunables;
|
||||
|
||||
Self::check_int(
|
||||
|
||||
@@ -321,7 +321,11 @@ impl fmt::Display for Trap {
|
||||
writeln!(f, "\nwasm backtrace:")?;
|
||||
for (i, frame) in self.trace().iter().enumerate() {
|
||||
let name = frame.module_name().unwrap_or("<unknown>");
|
||||
write!(f, " {:>3}: {:#6x} - ", i, frame.module_offset())?;
|
||||
write!(f, " {:>3}: ", i)?;
|
||||
|
||||
if let Some(offset) = frame.module_offset() {
|
||||
write!(f, "{:#6x} - ", offset)?;
|
||||
}
|
||||
|
||||
let demangle =
|
||||
|f: &mut fmt::Formatter<'_>, name: &str| match rustc_demangle::try_demangle(name) {
|
||||
|
||||
Reference in New Issue
Block a user