1 #[cfg(feature = "coredump")] 2 use super::coredump::WasmCoreDump; 3 #[cfg(feature = "gc")] 4 use crate::ThrownException; 5 use crate::prelude::*; 6 use crate::store::StoreOpaque; 7 use crate::{AsContext, Module}; 8 use core::fmt; 9 use wasmtime_environ::{FilePos, demangle_function_name, demangle_function_name_or_index}; 10 11 /// Representation of a WebAssembly trap and what caused it to occur. 12 /// 13 /// WebAssembly traps happen explicitly for instructions such as `unreachable` 14 /// but can also happen as side effects of other instructions such as `i32.load` 15 /// loading an out-of-bounds address. Traps halt the execution of WebAssembly 16 /// and cause an error to be returned to the host. This enumeration is a list of 17 /// all possible traps that can happen in wasm, in addition to some 18 /// Wasmtime-specific trap codes listed here as well. 19 /// 20 /// # Errors in Wasmtime 21 /// 22 /// Error-handling in Wasmtime is primarily done through the 23 /// [`wasmtime::Error`] type where most results are a 24 /// [`wasmtime::Result<T>`] which is an alias for [`Result<T, 25 /// wasmtime::Error>`](std::result::Result). Errors in Wasmtime are represented 26 /// with [`wasmtime::Error`] which acts as a container for any type of error in 27 /// addition to optional context for this error. The "base" error or 28 /// [`wasmtime::Error::root_cause`] is a [`Trap`] whenever WebAssembly hits a 29 /// trap, or otherwise it's whatever the host created the error with when 30 /// returning an error for a host call. 31 /// 32 /// Any error which happens while WebAssembly is executing will also, by 33 /// default, capture a backtrace of the wasm frames while executing. This 34 /// backtrace is represented with a [`WasmBacktrace`] instance and is attached 35 /// to the [`wasmtime::Error`] return value as a 36 /// [`context`](crate::Error::context). Inspecting a [`WasmBacktrace`] can be 37 /// done with the [`downcast_ref`](crate::Error::downcast_ref) function. For 38 /// information on this see the [`WasmBacktrace`] documentation. 39 /// 40 /// [`wasmtime::Error`]: crate::Error 41 /// [`wasmtime::Result<T>`]: crate::Result 42 /// [`wasmtime::Error::root_cause`]: crate::Error::root_cause 43 /// 44 /// # Examples 45 /// 46 /// ``` 47 /// # use wasmtime::*; 48 /// # fn main() -> Result<()> { 49 /// let engine = Engine::default(); 50 /// let module = Module::new( 51 /// &engine, 52 /// r#" 53 /// (module 54 /// (func (export "trap") 55 /// unreachable) 56 /// (func $overflow (export "overflow") 57 /// call $overflow) 58 /// ) 59 /// "#, 60 /// )?; 61 /// let mut store = Store::new(&engine, ()); 62 /// let instance = Instance::new(&mut store, &module, &[])?; 63 /// 64 /// let trap = instance.get_typed_func::<(), ()>(&mut store, "trap")?; 65 /// let error = trap.call(&mut store, ()).unwrap_err(); 66 /// assert_eq!(*error.downcast_ref::<Trap>().unwrap(), Trap::UnreachableCodeReached); 67 /// assert!(error.root_cause().is::<Trap>()); 68 /// 69 /// let overflow = instance.get_typed_func::<(), ()>(&mut store, "overflow")?; 70 /// let error = overflow.call(&mut store, ()).unwrap_err(); 71 /// assert_eq!(*error.downcast_ref::<Trap>().unwrap(), Trap::StackOverflow); 72 /// # Ok(()) 73 /// # } 74 /// ``` 75 pub use wasmtime_environ::Trap; 76 77 #[cold] // traps are exceptional, this helps move handling off the main path 78 pub(crate) fn from_runtime_box( 79 store: &mut StoreOpaque, 80 runtime_trap: Box<crate::runtime::vm::Trap>, 81 ) -> Error { 82 let crate::runtime::vm::Trap { 83 reason, 84 backtrace, 85 coredumpstack, 86 } = *runtime_trap; 87 let (mut error, pc) = match reason { 88 #[cfg(feature = "gc")] 89 crate::runtime::vm::TrapReason::Exception => (ThrownException.into(), None), 90 // For user-defined errors they're already an `crate::Error` so no 91 // conversion is really necessary here, but a `backtrace` may have 92 // been captured so it's attempted to get inserted here. 93 // 94 // If the error is actually a `Trap` then the backtrace is inserted 95 // directly into the `Trap` since there's storage there for it. 96 // Otherwise though this represents a host-defined error which isn't 97 // using a `Trap` but instead some other condition that was fatal to 98 // wasm itself. In that situation the backtrace is inserted as 99 // contextual information on error using `error.context(...)` to 100 // provide useful information to debug with for the embedder/caller, 101 // otherwise the information about what the wasm was doing when the 102 // error was generated would be lost. 103 crate::runtime::vm::TrapReason::User(error) => (error, None), 104 crate::runtime::vm::TrapReason::Jit { 105 pc, 106 faulting_addr, 107 trap, 108 } => { 109 let mut err: Error = trap.into(); 110 111 // If a fault address was present, for example with segfaults, 112 // then simultaneously assert that it's within a known linear memory 113 // and additionally translate it to a wasm-local address to be added 114 // as context to the error. 115 if let Some(fault) = faulting_addr.and_then(|addr| store.wasm_fault(pc, addr)) { 116 err = err.context(fault); 117 } 118 (err, Some(pc)) 119 } 120 crate::runtime::vm::TrapReason::Wasm(trap_code) => (trap_code.into(), None), 121 }; 122 123 if let Some(bt) = backtrace { 124 let bt = WasmBacktrace::from_captured(store, bt, pc); 125 if !bt.wasm_trace.is_empty() { 126 error = error.context(bt); 127 } 128 } 129 130 let _ = &coredumpstack; 131 #[cfg(feature = "coredump")] 132 if let Some(coredump) = coredumpstack { 133 let bt = WasmBacktrace::from_captured(store, coredump.bt, pc); 134 let cd = WasmCoreDump::new(store, bt); 135 error = error.context(cd); 136 } 137 138 error 139 } 140 141 /// Representation of a backtrace of function frames in a WebAssembly module for 142 /// where an error happened. 143 /// 144 /// This structure is attached to the [`wasmtime::Error`] returned from many 145 /// Wasmtime functions that execute WebAssembly such as [`Instance::new`] or 146 /// [`Func::call`]. This can be acquired with the 147 /// [`Error::downcast`](crate::Error::downcast) family of methods to 148 /// programmatically inspect the backtrace. Otherwise since it's part of the 149 /// error returned this will get printed along with the rest of the error when 150 /// the error is logged. 151 /// 152 /// Capturing of wasm backtraces can be configured through the 153 /// [`Config::wasm_backtrace`](crate::Config::wasm_backtrace) method. 154 /// 155 /// For more information about errors in wasmtime see the documentation of the 156 /// [`Trap`] type. 157 /// 158 /// [`Func::call`]: crate::Func::call 159 /// [`Instance::new`]: crate::Instance::new 160 /// [`wasmtime::Error`]: crate::Error 161 /// 162 /// # Examples 163 /// 164 /// ``` 165 /// # use wasmtime::*; 166 /// # fn main() -> Result<()> { 167 /// let engine = Engine::default(); 168 /// let module = Module::new( 169 /// &engine, 170 /// r#" 171 /// (module 172 /// (func $start (export "run") 173 /// call $trap) 174 /// (func $trap 175 /// unreachable) 176 /// ) 177 /// "#, 178 /// )?; 179 /// let mut store = Store::new(&engine, ()); 180 /// let instance = Instance::new(&mut store, &module, &[])?; 181 /// let func = instance.get_typed_func::<(), ()>(&mut store, "run")?; 182 /// let error = func.call(&mut store, ()).unwrap_err(); 183 /// let bt = error.downcast_ref::<WasmBacktrace>().unwrap(); 184 /// let frames = bt.frames(); 185 /// assert_eq!(frames.len(), 2); 186 /// assert_eq!(frames[0].func_name(), Some("trap")); 187 /// assert_eq!(frames[1].func_name(), Some("start")); 188 /// # Ok(()) 189 /// # } 190 /// ``` 191 #[derive(Debug)] 192 pub struct WasmBacktrace { 193 wasm_trace: Vec<FrameInfo>, 194 hint_wasm_backtrace_details_env: bool, 195 // This is currently only present for the `Debug` implementation for extra 196 // context. 197 _runtime_trace: crate::runtime::vm::Backtrace, 198 } 199 200 impl WasmBacktrace { 201 /// Captures a trace of the WebAssembly frames on the stack for the 202 /// provided store. 203 /// 204 /// This will return a [`WasmBacktrace`] which holds captured 205 /// [`FrameInfo`]s for each frame of WebAssembly on the call stack of the 206 /// current thread. If no WebAssembly is on the stack then the returned 207 /// backtrace will have no frames in it. 208 /// 209 /// Note that this function will respect the [`Config::wasm_backtrace`] 210 /// configuration option and will return an empty backtrace if that is 211 /// disabled. To always capture a backtrace use the 212 /// [`WasmBacktrace::force_capture`] method. 213 /// 214 /// Also note that this function will only capture frames from the 215 /// specified `store` on the stack, ignoring frames from other stores if 216 /// present. 217 /// 218 /// [`Config::wasm_backtrace`]: crate::Config::wasm_backtrace 219 /// 220 /// # Example 221 /// 222 /// ``` 223 /// # use wasmtime::*; 224 /// # fn main() -> Result<()> { 225 /// let engine = Engine::default(); 226 /// let module = Module::new( 227 /// &engine, 228 /// r#" 229 /// (module 230 /// (import "" "" (func $host)) 231 /// (func $foo (export "f") call $bar) 232 /// (func $bar call $host) 233 /// ) 234 /// "#, 235 /// )?; 236 /// 237 /// let mut store = Store::new(&engine, ()); 238 /// let func = Func::wrap(&mut store, |cx: Caller<'_, ()>| { 239 /// let trace = WasmBacktrace::capture(&cx); 240 /// println!("{trace:?}"); 241 /// }); 242 /// let instance = Instance::new(&mut store, &module, &[func.into()])?; 243 /// let func = instance.get_typed_func::<(), ()>(&mut store, "f")?; 244 /// func.call(&mut store, ())?; 245 /// # Ok(()) 246 /// # } 247 /// ``` 248 pub fn capture(store: impl AsContext) -> WasmBacktrace { 249 let store = store.as_context(); 250 if store.engine().config().wasm_backtrace { 251 Self::force_capture(store) 252 } else { 253 WasmBacktrace { 254 wasm_trace: Vec::new(), 255 hint_wasm_backtrace_details_env: false, 256 _runtime_trace: crate::runtime::vm::Backtrace::empty(), 257 } 258 } 259 } 260 261 /// Unconditionally captures a trace of the WebAssembly frames on the stack 262 /// for the provided store. 263 /// 264 /// Same as [`WasmBacktrace::capture`] except that it disregards the 265 /// [`Config::wasm_backtrace`](crate::Config::wasm_backtrace) setting and 266 /// always captures a backtrace. 267 pub fn force_capture(store: impl AsContext) -> WasmBacktrace { 268 let store = store.as_context(); 269 Self::from_captured(store.0, crate::runtime::vm::Backtrace::new(store.0), None) 270 } 271 272 fn from_captured( 273 store: &StoreOpaque, 274 runtime_trace: crate::runtime::vm::Backtrace, 275 trap_pc: Option<usize>, 276 ) -> Self { 277 let mut wasm_trace = Vec::<FrameInfo>::with_capacity(runtime_trace.frames().len()); 278 let mut hint_wasm_backtrace_details_env = false; 279 let wasm_backtrace_details_env_used = 280 store.engine().config().wasm_backtrace_details_env_used; 281 282 for frame in runtime_trace.frames() { 283 debug_assert!(frame.pc() != 0); 284 285 // Note that we need to be careful about the pc we pass in 286 // here to lookup frame information. This program counter is 287 // used to translate back to an original source location in 288 // the origin wasm module. If this pc is the exact pc that 289 // the trap happened at, then we look up that pc precisely. 290 // Otherwise backtrace information typically points at the 291 // pc *after* the call instruction (because otherwise it's 292 // likely a call instruction on the stack). In that case we 293 // want to lookup information for the previous instruction 294 // (the call instruction) so we subtract one as the lookup. 295 let pc_to_lookup = if Some(frame.pc()) == trap_pc { 296 frame.pc() 297 } else { 298 frame.pc() - 1 299 }; 300 301 // NB: The PC we are looking up _must_ be a Wasm PC since 302 // `crate::runtime::vm::Backtrace` only contains Wasm frames. 303 // 304 // However, consider the case where we have multiple, nested calls 305 // across stores (with host code in between, by necessity, since 306 // only things in the same store can be linked directly together): 307 // 308 // | ... | 309 // | Host | | 310 // +-----------------+ | stack 311 // | Wasm in store A | | grows 312 // +-----------------+ | down 313 // | Host | | 314 // +-----------------+ | 315 // | Wasm in store B | V 316 // +-----------------+ 317 // 318 // In this scenario, the `crate::runtime::vm::Backtrace` will 319 // contain two frames: Wasm in store B followed by Wasm in store 320 // A. But `store.modules()` will only have the module information 321 // for modules instantiated within this store. Therefore, we use `if 322 // let Some(..)` instead of the `unwrap` you might otherwise expect 323 // and we ignore frames from modules that were not registered in 324 // this store's module registry. 325 if let Some((info, module)) = store.modules().lookup_frame_info(pc_to_lookup) { 326 wasm_trace.push(info); 327 328 // If this frame has unparsed debug information and the 329 // store's configuration indicates that we were 330 // respecting the environment variable of whether to 331 // do this then we will print out a helpful note in 332 // `Display` to indicate that more detailed information 333 // in a trap may be available. 334 let has_unparsed_debuginfo = 335 module.module().compiled_module().has_unparsed_debuginfo(); 336 if has_unparsed_debuginfo 337 && wasm_backtrace_details_env_used 338 && cfg!(feature = "addr2line") 339 { 340 hint_wasm_backtrace_details_env = true; 341 } 342 } 343 } 344 345 Self { 346 wasm_trace, 347 _runtime_trace: runtime_trace, 348 hint_wasm_backtrace_details_env, 349 } 350 } 351 352 /// Returns a list of function frames in WebAssembly this backtrace 353 /// represents. 354 pub fn frames(&self) -> &[FrameInfo] { 355 self.wasm_trace.as_slice() 356 } 357 } 358 359 impl fmt::Display for WasmBacktrace { 360 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 361 writeln!(f, "error while executing at wasm backtrace:")?; 362 363 let mut needs_newline = false; 364 for (i, frame) in self.wasm_trace.iter().enumerate() { 365 // Avoid putting a trailing newline on the output 366 if needs_newline { 367 writeln!(f, "")?; 368 } else { 369 needs_newline = true; 370 } 371 let name = frame.module().name().unwrap_or("<unknown>"); 372 write!(f, " {i:>3}: ")?; 373 374 if let Some(offset) = frame.module_offset() { 375 write!(f, "{offset:#8x} - ")?; 376 } 377 378 let write_raw_func_name = |f: &mut fmt::Formatter<'_>| { 379 demangle_function_name_or_index(f, frame.func_name(), frame.func_index() as usize) 380 }; 381 if frame.symbols().is_empty() { 382 write!(f, "{name}!")?; 383 write_raw_func_name(f)?; 384 } else { 385 for (i, symbol) in frame.symbols().iter().enumerate() { 386 if i > 0 { 387 if needs_newline { 388 writeln!(f, "")?; 389 } else { 390 needs_newline = true; 391 } 392 write!(f, " - ")?; 393 } else { 394 // ... 395 } 396 match symbol.name() { 397 Some(name) => demangle_function_name(f, name)?, 398 None if i == 0 => write_raw_func_name(f)?, 399 None => write!(f, "<inlined function>")?, 400 } 401 if let Some(file) = symbol.file() { 402 writeln!(f, "")?; 403 write!(f, " at {file}")?; 404 if let Some(line) = symbol.line() { 405 write!(f, ":{line}")?; 406 if let Some(col) = symbol.column() { 407 write!(f, ":{col}")?; 408 } 409 } 410 } 411 } 412 } 413 } 414 if self.hint_wasm_backtrace_details_env { 415 write!( 416 f, 417 "\nnote: using the `WASMTIME_BACKTRACE_DETAILS=1` \ 418 environment variable may show more debugging information" 419 )?; 420 } 421 Ok(()) 422 } 423 } 424 425 /// Description of a frame in a backtrace for a [`WasmBacktrace`]. 426 /// 427 /// Whenever an error happens while WebAssembly is executing a 428 /// [`WasmBacktrace`] will be attached to the error returned which can be used 429 /// to acquire this `FrameInfo`. For more information see [`WasmBacktrace`]. 430 #[derive(Debug)] 431 pub struct FrameInfo { 432 module: Module, 433 func_index: u32, 434 func_name: Option<String>, 435 func_start: FilePos, 436 instr: Option<FilePos>, 437 symbols: Vec<FrameSymbol>, 438 } 439 440 impl FrameInfo { 441 /// Fetches frame information about a program counter in a backtrace. 442 /// 443 /// Returns an object if this `pc` is known to this module, or returns `None` 444 /// if no information can be found. 445 pub(crate) fn new(module: Module, text_offset: usize) -> Option<FrameInfo> { 446 let compiled_module = module.compiled_module(); 447 let index = compiled_module.func_by_text_offset(text_offset)?; 448 let func_start = compiled_module.func_start_srcloc(index); 449 let instr = 450 wasmtime_environ::lookup_file_pos(module.engine_code().address_map_data(), text_offset); 451 let index = compiled_module.module().func_index(index); 452 let func_index = index.as_u32(); 453 let func_name = compiled_module.func_name(index).map(|s| s.to_string()); 454 455 // In debug mode for now assert that we found a mapping for `pc` within 456 // the function, because otherwise something is buggy along the way and 457 // not accounting for all the instructions. This isn't super critical 458 // though so we can omit this check in release mode. 459 // 460 // Note that if the module doesn't even have an address map due to 461 // compilation settings then it's expected that `instr` is `None`. 462 debug_assert!( 463 instr.is_some() || !compiled_module.has_address_map(), 464 "failed to find instruction for {text_offset:#x}" 465 ); 466 467 // Use our wasm-relative pc to symbolize this frame. If there's a 468 // symbolication context (dwarf debug info) available then we can try to 469 // look this up there. 470 // 471 // Note that dwarf pcs are code-section-relative, hence the subtraction 472 // from the location of `instr`. Also note that all errors are ignored 473 // here for now since technically wasm modules can always have any 474 // custom section contents. 475 let mut symbols = Vec::new(); 476 477 let _ = &mut symbols; 478 #[cfg(feature = "addr2line")] 479 if let Some(s) = &compiled_module.symbolize_context().ok().and_then(|c| c) { 480 if let Some(offset) = instr.and_then(|i| i.file_offset()) { 481 let to_lookup = u64::from(offset) - s.code_section_offset(); 482 if let Ok(mut frames) = s.addr2line().find_frames(to_lookup).skip_all_loads() { 483 while let Ok(Some(frame)) = frames.next() { 484 symbols.push(FrameSymbol { 485 name: frame 486 .function 487 .as_ref() 488 .and_then(|l| l.raw_name().ok()) 489 .map(|s| s.to_string()), 490 file: frame 491 .location 492 .as_ref() 493 .and_then(|l| l.file) 494 .map(|s| s.to_string()), 495 line: frame.location.as_ref().and_then(|l| l.line), 496 column: frame.location.as_ref().and_then(|l| l.column), 497 }); 498 } 499 } 500 } 501 } 502 503 Some(FrameInfo { 504 module, 505 func_index, 506 func_name, 507 instr, 508 func_start, 509 symbols, 510 }) 511 } 512 513 /// Returns the WebAssembly function index for this frame. 514 /// 515 /// This function index is the index in the function index space of the 516 /// WebAssembly module that this frame comes from. 517 pub fn func_index(&self) -> u32 { 518 self.func_index 519 } 520 521 /// Returns the module for this frame. 522 /// 523 /// This is the module who's code was being run in this frame. 524 pub fn module(&self) -> &Module { 525 &self.module 526 } 527 528 /// Returns a descriptive name of the function for this frame, if one is 529 /// available. 530 /// 531 /// The name of this function may come from the `name` section of the 532 /// WebAssembly binary, or wasmtime may try to infer a better name for it if 533 /// not available, for example the name of the export if it's exported. 534 /// 535 /// This return value is primarily used for debugging and human-readable 536 /// purposes for things like traps. Note that the exact return value may be 537 /// tweaked over time here and isn't guaranteed to be something in 538 /// particular about a wasm module due to its primary purpose of assisting 539 /// in debugging. 540 /// 541 /// This function returns `None` when no name could be inferred. 542 pub fn func_name(&self) -> Option<&str> { 543 self.func_name.as_deref() 544 } 545 546 /// Returns the offset within the original wasm module this frame's program 547 /// counter was at. 548 /// 549 /// The offset here is the offset from the beginning of the original wasm 550 /// module to the instruction that this frame points to. 551 /// 552 /// Note that `None` may be returned if the original module was not 553 /// compiled with mapping information to yield this information. This is 554 /// controlled by the 555 /// [`Config::generate_address_map`](crate::Config::generate_address_map) 556 /// configuration option. 557 pub fn module_offset(&self) -> Option<usize> { 558 Some(self.instr?.file_offset()? as usize) 559 } 560 561 /// Returns the offset from the original wasm module's function to this 562 /// frame's program counter. 563 /// 564 /// The offset here is the offset from the beginning of the defining 565 /// function of this frame (within the wasm module) to the instruction this 566 /// frame points to. 567 /// 568 /// Note that `None` may be returned if the original module was not 569 /// compiled with mapping information to yield this information. This is 570 /// controlled by the 571 /// [`Config::generate_address_map`](crate::Config::generate_address_map) 572 /// configuration option. 573 pub fn func_offset(&self) -> Option<usize> { 574 let instr_offset = self.instr?.file_offset()?; 575 Some((instr_offset - self.func_start.file_offset()?) as usize) 576 } 577 578 /// Returns the debug symbols found, if any, for this function frame. 579 /// 580 /// When a wasm program is compiled with DWARF debug information then this 581 /// function may be populated to return symbols which contain extra debug 582 /// information about a frame including the filename and line number. If no 583 /// debug information was found or if it was malformed then this will return 584 /// an empty array. 585 pub fn symbols(&self) -> &[FrameSymbol] { 586 &self.symbols 587 } 588 } 589 590 /// Debug information for a symbol that is attached to a [`FrameInfo`]. 591 /// 592 /// When DWARF debug information is present in a wasm file then this structure 593 /// can be found on a [`FrameInfo`] and can be used to learn about filenames, 594 /// line numbers, etc, which are the origin of a function in a stack trace. 595 #[derive(Debug)] 596 pub struct FrameSymbol { 597 name: Option<String>, 598 file: Option<String>, 599 line: Option<u32>, 600 column: Option<u32>, 601 } 602 603 impl FrameSymbol { 604 /// Returns the function name associated with this symbol. 605 /// 606 /// Note that this may not be present with malformed debug information, or 607 /// the debug information may not include it. Also note that the symbol is 608 /// frequently mangled, so you might need to run some form of demangling 609 /// over it. 610 pub fn name(&self) -> Option<&str> { 611 self.name.as_deref() 612 } 613 614 /// Returns the source code filename this symbol was defined in. 615 /// 616 /// Note that this may not be present with malformed debug information, or 617 /// the debug information may not include it. 618 pub fn file(&self) -> Option<&str> { 619 self.file.as_deref() 620 } 621 622 /// Returns the 1-indexed source code line number this symbol was defined 623 /// on. 624 /// 625 /// Note that this may not be present with malformed debug information, or 626 /// the debug information may not include it. 627 pub fn line(&self) -> Option<u32> { 628 self.line 629 } 630 631 /// Returns the 1-indexed source code column number this symbol was defined 632 /// on. 633 /// 634 /// Note that this may not be present with malformed debug information, or 635 /// the debug information may not include it. 636 pub fn column(&self) -> Option<u32> { 637 self.column 638 } 639 } 640