marwood/vm/
trace.rs

1use crate::cell::Cell;
2use crate::vm::heap::Heap;
3use crate::vm::opcode::OpCode;
4use crate::vm::stack::Stack;
5use crate::vm::vcell::VCell;
6
7/// Stack Frame
8///
9/// Stack Frame represents one stack frame in a backtrace. It includes a
10/// description of the applied procedure, and the arguments pushed to the
11/// procedure in order.
12#[derive(Debug)]
13pub struct StackFrame {
14    pub name: Option<String>,
15    pub desc: Option<Cell>,
16}
17
18/// Stack Trace
19///
20/// Stack trace represents a decoded stack trace in order of procedure
21/// application, frames[0] containing the entry procedure.
22#[derive(Debug)]
23pub struct StackTrace {
24    pub frames: Vec<StackFrame>,
25}
26
27impl StackTrace {
28    /// New
29    ///
30    /// Given a stack, heap and %ip register, construct a stack trace.
31    pub fn new(stack: &Stack, heap: &Heap, ip: (usize, usize), acc: VCell) -> StackTrace {
32        let mut frames = vec![];
33
34        // Get currently running lambda
35        let mut ip_idx = ip.1;
36        let ip = heap.get_at_index(ip.0).as_lambda().unwrap();
37
38        // Reverse %ip to last instruction
39        ip_idx -= 1;
40        while ip_idx > 0 && !matches!(ip.get(ip_idx).unwrap(), VCell::OpCode(_)) {
41            ip_idx -= 1;
42        }
43        let op_code = ip.get(ip_idx).unwrap().as_opcode().unwrap();
44
45        // If the just executed instruction was procedure application for a builtin, then
46        // the builtin is the top frame.
47        match op_code {
48            OpCode::TCallAcc | OpCode::CallAcc => {
49                if let VCell::BuiltInProc(proc) = heap.get(&acc) {
50                    frames.push(StackFrame {
51                        name: Some(proc.desc().to_owned()),
52                        desc: None,
53                    })
54                }
55            }
56            _ => {}
57        }
58
59        // Push the currently executing lambda onto the frames
60        frames.push(StackFrame {
61            name: None,
62            desc: ip.desc_args.clone(),
63        });
64
65        // Iterate the stack backwards
66        for sp in (0..stack.get_sp()).rev() {
67            if let Ok(VCell::InstructionPointer(ip, _)) = stack.get(sp) {
68                let ip = heap.get_at_index(*ip).as_lambda().unwrap();
69                frames.push(StackFrame {
70                    name: None,
71                    desc: ip.desc_args.clone(),
72                });
73            }
74        }
75
76        StackTrace { frames }
77    }
78}