sphinx/debug/
traceback.rs1use core::fmt;
2use crate::source::ModuleSource;
3use crate::runtime::gc::{Gc, GcTrace};
4use crate::runtime::module::{Module, Chunk};
5
6
7#[derive(Debug, Clone)]
9pub enum TraceSite {
10 Chunk {
11 offset: usize,
12 module: Gc<Module>,
13 chunk_id: Chunk,
14 },
15 Native, }
17
18unsafe impl GcTrace for TraceSite {
19 fn trace(&self) {
20 if let Self::Chunk { module, .. } = self {
21 module.mark_trace();
22 }
23 }
24}
25
26
27pub struct FrameSummary<'a> {
28 trace: &'a TraceSite,
29}
30
31impl fmt::Display for FrameSummary<'_> {
32 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
33 match self.trace {
34 TraceSite::Chunk { offset, module, chunk_id } => {
35 let module_desc = module_desc(module);
36 let loc_desc = format!("<@{:#X}>", offset);
37 let chunk_desc = chunk_desc(module, chunk_id);
38 write!(fmt, "{}, {} in {}", module_desc, loc_desc, chunk_desc)
39 },
40
41 TraceSite::Native => {
42 write!(fmt, "<native code>")
43 },
44 }
45 }
46}
47
48fn module_desc(module: &Module) -> String {
49 if let Some(ModuleSource::File(path)) = module.source() {
50 format!("File \"{}\"", path.display())
51 } else {
52 "<anonymous module>".to_string()
53 }
54}
55
56fn chunk_desc(module: &Module, chunk_id: &Chunk) -> String {
57 match chunk_id {
58 Chunk::Main => "<module>".to_string(),
59
60 Chunk::Function(fun_id) => {
61 let function = module.data().get_function(*fun_id);
62 format!("{}", function.signature())
63 },
64 }
65}
66
67
68pub struct Traceback<'a> {
69 frames: Vec<FrameSummary<'a>>,
70}
71
72
73impl<'a> Traceback<'a> {
74 pub fn build(trace: impl Iterator<Item=&'a TraceSite>) -> Self {
75 Self {
76 frames: trace.map(|trace| FrameSummary { trace }).collect(),
77 }
78 }
79}
80
81impl fmt::Display for Traceback<'_> {
82 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
83 fmt.write_str("Stack trace (most recent call last):\n")?;
84
85 for (idx, frame) in self.frames.iter().enumerate().rev() {
86 writeln!(fmt, "#{} {}", idx + 1, frame)?;
87 }
88
89 Ok(())
90 }
91}