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