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