about summary refs log tree commit diff
path: root/tvix/eval
diff options
context:
space:
mode:
authorVincent Ambo <mail@tazj.in>2022-09-04T16·38+0300
committertazjin <tazjin@tvl.su>2022-09-09T21·10+0000
commit14ff889d607635083a030fc73d76b0263759be83 (patch)
tree42b84f7ea79a51ffe64b033ef105e6f04738733e /tvix/eval
parentcbf2d2d29293af56d60fa7e04ee1969c18b9845f (diff)
feat(tvix/eval): implement runtime tracing methods for Observer r/4778
These methods make it possible to trace the runtime execution of the
VM through an observer.

Change-Id: I90e26853ba2fe44748613e7f761ed5c1c5fc9ff7
Reviewed-on: https://cl.tvl.fyi/c/depot/+/6452
Reviewed-by: sterni <sternenseemann@systemli.org>
Tested-by: BuildkiteCI
Diffstat (limited to 'tvix/eval')
-rw-r--r--tvix/eval/src/eval.rs5
-rw-r--r--tvix/eval/src/observer.rs19
-rw-r--r--tvix/eval/src/vm.rs50
3 files changed, 44 insertions, 30 deletions
diff --git a/tvix/eval/src/eval.rs b/tvix/eval/src/eval.rs
index f510cb6a5eaf..47c7203d28ac 100644
--- a/tvix/eval/src/eval.rs
+++ b/tvix/eval/src/eval.rs
@@ -3,7 +3,7 @@ use std::{path::PathBuf, rc::Rc};
 use crate::{
     builtins::global_builtins,
     errors::{Error, ErrorKind, EvalResult},
-    observer::DisassemblingObserver,
+    observer::{DisassemblingObserver, NoOpObserver},
     value::Value,
 };
 
@@ -68,5 +68,6 @@ pub fn interpret(code: &str, location: Option<PathBuf>) -> EvalResult<Value> {
         return Err(err.clone());
     }
 
-    crate::vm::run_lambda(result.lambda)
+    let mut tracer = NoOpObserver::default();
+    crate::vm::run_lambda(&mut tracer, result.lambda)
 }
diff --git a/tvix/eval/src/observer.rs b/tvix/eval/src/observer.rs
index 62312a74ad36..427fc2c3996a 100644
--- a/tvix/eval/src/observer.rs
+++ b/tvix/eval/src/observer.rs
@@ -10,8 +10,9 @@ use std::rc::Rc;
 use tabwriter::TabWriter;
 
 use crate::chunk::Chunk;
-use crate::opcode::CodeIdx;
+use crate::opcode::{CodeIdx, OpCode};
 use crate::value::Lambda;
+use crate::Value;
 
 /// Implemented by types that wish to observe internal happenings of
 /// Tvix.
@@ -33,6 +34,22 @@ pub trait Observer {
 
     /// Called when the compiler finishes compilation of a thunk.
     fn observe_compiled_thunk(&mut self, _: &Rc<Lambda>) {}
+
+    /// Called when the runtime enters a new call frame.
+    fn observe_enter_frame(&mut self, _arg_count: usize, _: &Rc<Lambda>, _call_depth: usize) {}
+
+    /// Called when the runtime exits a call frame.
+    fn observe_exit_frame(&mut self, _frame_at: usize) {}
+
+    /// Called when the runtime enters a builtin.
+    fn observe_enter_builtin(&mut self, _name: &'static str) {}
+
+    /// Called when the runtime exits a builtin.
+    fn observe_exit_builtin(&mut self, _name: &'static str) {}
+
+    /// Called when the runtime *begins* executing an instruction. The
+    /// provided stack is the state at the beginning of the operation.
+    fn observe_execute_op(&mut self, _ip: usize, _: &OpCode, _: &[Value]) {}
 }
 
 #[derive(Default)]
diff --git a/tvix/eval/src/vm.rs b/tvix/eval/src/vm.rs
index b9445e39f1b1..73a03263c864 100644
--- a/tvix/eval/src/vm.rs
+++ b/tvix/eval/src/vm.rs
@@ -6,6 +6,7 @@ use std::{cell::RefMut, rc::Rc};
 use crate::{
     chunk::Chunk,
     errors::{Error, ErrorKind, EvalResult},
+    observer::Observer,
     opcode::{CodeIdx, ConstantIdx, Count, JumpOffset, OpCode, StackIdx, UpvalueIdx},
     upvalues::UpvalueCarrier,
     value::{Closure, Lambda, NixAttrs, NixList, Thunk, Value},
@@ -25,8 +26,7 @@ impl CallFrame {
     }
 }
 
-#[derive(Default)]
-pub struct VM {
+pub struct VM<'o> {
     frames: Vec<CallFrame>,
     stack: Vec<Value>,
 
@@ -34,8 +34,7 @@ pub struct VM {
     // dynamically resolved (`with`).
     with_stack: Vec<usize>,
 
-    #[cfg(feature = "disassembler")]
-    pub tracer: crate::disassembler::Tracer,
+    observer: &'o mut dyn Observer,
 }
 
 /// This macro wraps a computation that returns an ErrorKind or a
@@ -111,7 +110,16 @@ macro_rules! cmp_op {
     }};
 }
 
-impl VM {
+impl<'o> VM<'o> {
+    pub fn new(observer: &'o mut dyn Observer) -> Self {
+        Self {
+            observer,
+            frames: vec![],
+            stack: vec![],
+            with_stack: vec![],
+        }
+    }
+
     fn frame(&self) -> &CallFrame {
         &self.frames[self.frames.len() - 1]
     }
@@ -171,13 +179,8 @@ impl VM {
         upvalues: Vec<Value>,
         arg_count: usize,
     ) -> EvalResult<Value> {
-        #[cfg(feature = "disassembler")]
-        self.tracer.literal(&format!(
-            "=== entering closure/{} @ {:p} [{}] ===",
-            arg_count,
-            lambda,
-            self.frames.len()
-        ));
+        self.observer
+            .observe_enter_frame(arg_count, &lambda, self.frames.len() + 1);
 
         let frame = CallFrame {
             lambda,
@@ -189,12 +192,7 @@ impl VM {
         self.frames.push(frame);
         let result = self.run();
 
-        #[cfg(feature = "disassembler")]
-        self.tracer.literal(&format!(
-            "=== exiting closure/{} [{}] ===",
-            arg_count,
-            self.frames.len()
-        ));
+        self.observer.observe_exit_frame(self.frames.len() + 1);
 
         result
     }
@@ -213,8 +211,8 @@ impl VM {
 
             let op = self.inc_ip();
 
-            #[cfg(feature = "disassembler")]
-            self.tracer.trace(&op, self.frame().ip, &self.stack);
+            self.observer
+                .observe_execute_op(self.frame().ip, &op, &self.stack);
 
             match op {
                 OpCode::OpConstant(idx) => {
@@ -450,15 +448,13 @@ impl VM {
                         }
 
                         Value::Builtin(builtin) => {
-                            #[cfg(feature = "disassembler")]
-                            self.tracer
-                                .literal(&format!("=== entering builtins.{} ===", builtin.name()));
+                            let builtin_name = builtin.name();
+                            self.observer.observe_enter_builtin(builtin_name);
 
                             let arg = self.pop();
                             let result = fallible!(self, builtin.apply(self, arg));
 
-                            #[cfg(feature = "disassembler")]
-                            self.tracer.literal("=== exiting builtin ===");
+                            self.observer.observe_exit_builtin(builtin_name);
 
                             self.push(result);
                         }
@@ -711,8 +707,8 @@ fn unwrap_or_clone_rc<T: Clone>(rc: Rc<T>) -> T {
     Rc::try_unwrap(rc).unwrap_or_else(|rc| (*rc).clone())
 }
 
-pub fn run_lambda(lambda: Rc<Lambda>) -> EvalResult<Value> {
-    let mut vm = VM::default();
+pub fn run_lambda(observer: &mut dyn Observer, lambda: Rc<Lambda>) -> EvalResult<Value> {
+    let mut vm = VM::new(observer);
     let value = vm.call(lambda, vec![], 0)?;
     vm.force_for_output(&value)?;
     Ok(value)