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