stak_profiler/
stack_profiler.rs

1use crate::{COLUMN_SEPARATOR, FRAME_SEPARATOR, ProcedureOperation};
2use stak_vm::{Cons, Error, Heap, Memory, Profiler, StackSlot};
3use std::{io::Write, time::Instant};
4
5/// A stack profiler.
6pub struct StackProfiler<T: Write> {
7    writer: T,
8    start_time: Instant,
9}
10
11impl<T: Write> StackProfiler<T> {
12    /// Creates a stack profiler.
13    pub fn new(writer: T) -> Self {
14        Self {
15            writer,
16            start_time: Instant::now(),
17        }
18    }
19
20    fn write_column_separator(&mut self) {
21        write!(self.writer, "{COLUMN_SEPARATOR}").unwrap();
22    }
23
24    fn write_frame_separator(&mut self) {
25        write!(self.writer, "{FRAME_SEPARATOR}").unwrap();
26    }
27
28    fn write_time(&mut self) {
29        writeln!(
30            &mut self.writer,
31            "{}",
32            Instant::now().duration_since(self.start_time).as_nanos()
33        )
34        .unwrap();
35    }
36
37    fn write_procedure<H: Heap>(&mut self, memory: &Memory<H>, code: Cons) -> Result<(), Error> {
38        let operand = memory.car(code)?;
39
40        if let Some(symbol) = operand.to_cons() {
41            let mut string = memory.cdr_value(memory.cdr(symbol)?)?.assume_cons();
42
43            while string != memory.null()? {
44                write!(
45                    self.writer,
46                    "{}",
47                    char::from_u32(memory.car(string)?.assume_number().to_i64() as _)
48                        .unwrap_or('�')
49                )
50                .unwrap();
51                string = memory.cdr(string)?.assume_cons();
52            }
53        }
54
55        Ok(())
56    }
57
58    fn write_stack<H: Heap>(&mut self, memory: &Memory<H>) -> Result<(), Error> {
59        let mut stack = memory.stack();
60        let mut first = true;
61
62        while stack != memory.null()? {
63            stack = if memory.cdr(stack)?.tag() == StackSlot::Frame as _ {
64                if !first {
65                    self.write_frame_separator();
66                }
67
68                first = false;
69
70                self.write_procedure(memory, memory.car_value(memory.car(stack)?)?.assume_cons())?;
71
72                memory.cdr_value(memory.car(stack)?)?.assume_cons()
73            } else {
74                memory.cdr(stack)?.assume_cons()
75            };
76        }
77
78        Ok(())
79    }
80}
81
82impl<T: Write, H: Heap> Profiler<H> for StackProfiler<T> {
83    fn profile_call(
84        &mut self,
85        memory: &Memory<H>,
86        call_code: Cons,
87        r#return: bool,
88    ) -> Result<(), Error> {
89        write!(
90            self.writer,
91            "{}",
92            if r#return {
93                ProcedureOperation::ReturnCall
94            } else {
95                ProcedureOperation::Call
96            }
97        )
98        .unwrap();
99        self.write_column_separator();
100        self.write_procedure(memory, call_code)?;
101        self.write_frame_separator();
102        self.write_stack(memory)?;
103        self.write_column_separator();
104        self.write_time();
105
106        Ok(())
107    }
108
109    fn profile_return(&mut self, memory: &Memory<H>) -> Result<(), Error> {
110        write!(self.writer, "{}", ProcedureOperation::Return).unwrap();
111        self.write_column_separator();
112        self.write_stack(memory)?;
113        self.write_column_separator();
114        self.write_time();
115
116        Ok(())
117    }
118
119    fn profile_event(&mut self, name: &str) -> Result<(), Error> {
120        write!(self.writer, "{name}").unwrap();
121        self.write_column_separator();
122        self.write_time();
123
124        Ok(())
125    }
126}