diff options
author | Vincent Ambo <mail@tazj.in> | 2023-03-03T21·52+0300 |
---|---|---|
committer | tazjin <tazjin@tvl.su> | 2023-03-13T20·30+0000 |
commit | 1e37f8b52e3d42fed3e05b327ef30c83e97fd02a (patch) | |
tree | c05ece48e6d2d01d27694ee0949629616bdec611 /tvix/eval/src/vm | |
parent | dfd0066de5f0616673f8e6cef4c6bed312e98d1e (diff) |
feat(tvix/eval): give generators human-readable names r/5978
This adds static strings to generator frames that describe the generator in a human-readable fashion, which are then logged in observers. This makes runtime traces very precise, explaining exactly what is being requested from where. Change-Id: I695659a6bd0b7b0bdee75bc8049651f62b150e0c Reviewed-on: https://cl.tvl.fyi/c/depot/+/8206 Tested-by: BuildkiteCI Reviewed-by: raitobezarius <tvl@lahfa.xyz>
Diffstat (limited to 'tvix/eval/src/vm')
-rw-r--r-- | tvix/eval/src/vm/generators.rs | 45 | ||||
-rw-r--r-- | tvix/eval/src/vm/macros.rs | 2 | ||||
-rw-r--r-- | tvix/eval/src/vm/mod.rs | 44 |
3 files changed, 56 insertions, 35 deletions
diff --git a/tvix/eval/src/vm/generators.rs b/tvix/eval/src/vm/generators.rs index 1a9743dbd960..7a1ba0153d24 100644 --- a/tvix/eval/src/vm/generators.rs +++ b/tvix/eval/src/vm/generators.rs @@ -217,8 +217,9 @@ pub fn pin_generator( impl<'o> VM<'o> { /// Helper function to re-enqueue the current generator while it /// is awaiting a value. - fn reenqueue_generator(&mut self, span: LightSpan, generator: Generator) { + fn reenqueue_generator(&mut self, name: &'static str, span: LightSpan, generator: Generator) { self.frames.push(Frame::Generator { + name, generator, span, state: GeneratorState::AwaitingValue, @@ -226,12 +227,13 @@ impl<'o> VM<'o> { } /// Helper function to enqueue a new generator. - pub(super) fn enqueue_generator<F, G>(&mut self, span: LightSpan, gen: G) + pub(super) fn enqueue_generator<F, G>(&mut self, name: &'static str, span: LightSpan, gen: G) where F: Future<Output = Result<Value, ErrorKind>> + 'static, G: FnOnce(GenCo) -> F, { self.frames.push(Frame::Generator { + name, span, state: GeneratorState::Running, generator: Gen::new(|co| pin_generator(gen(co))), @@ -245,6 +247,7 @@ impl<'o> VM<'o> { /// or was suspended (false). pub(crate) fn run_generator( &mut self, + name: &'static str, span: LightSpan, frame_id: usize, state: GeneratorState, @@ -266,7 +269,7 @@ impl<'o> VM<'o> { // If the generator yields, it contains an instruction // for what the VM should do. genawaiter::GeneratorState::Yielded(request) => { - self.observer.observe_generator_request(&request); + self.observer.observe_generator_request(name, &request); match request { GeneratorRequest::StackPush(value) => { @@ -282,15 +285,17 @@ impl<'o> VM<'o> { // this function prepares the frame stack and yields // back to the outer VM loop. GeneratorRequest::ForceValue(value) => { - self.reenqueue_generator(span.clone(), generator); - self.enqueue_generator(span, |co| value.force(co)); + self.reenqueue_generator(name, span.clone(), generator); + self.enqueue_generator("force", span, |co| value.force(co)); return Ok(false); } // Generator has requested a deep-force. GeneratorRequest::DeepForceValue(value, thunk_set) => { - self.reenqueue_generator(span.clone(), generator); - self.enqueue_generator(span, |co| value.deep_force(co, thunk_set)); + self.reenqueue_generator(name, span.clone(), generator); + self.enqueue_generator("deep_force", span, |co| { + value.deep_force(co, thunk_set) + }); return Ok(false); } @@ -298,10 +303,10 @@ impl<'o> VM<'o> { // Logic is similar to `ForceValue`, except with the // value being taken from that stack. GeneratorRequest::WithValue(idx) => { - self.reenqueue_generator(span.clone(), generator); + self.reenqueue_generator(name, span.clone(), generator); let value = self.stack[self.with_stack[idx]].clone(); - self.enqueue_generator(span, |co| value.force(co)); + self.enqueue_generator("force", span, |co| value.force(co)); return Ok(false); } @@ -310,34 +315,36 @@ impl<'o> VM<'o> { // with-stack. Logic is same as above, except for the // value being from that stack. GeneratorRequest::CapturedWithValue(idx) => { - self.reenqueue_generator(span.clone(), generator); + self.reenqueue_generator(name, span.clone(), generator); let call_frame = self.last_call_frame() .expect("Tvix bug: generator requested captured with-value, but there is no call frame"); let value = call_frame.upvalues.with_stack().unwrap()[idx].clone(); - self.enqueue_generator(span, |co| value.force(co)); + self.enqueue_generator("force", span, |co| value.force(co)); return Ok(false); } GeneratorRequest::NixEquality(values, ptr_eq) => { let values = *values; - self.reenqueue_generator(span.clone(), generator); - self.enqueue_generator(span, |co| { + self.reenqueue_generator(name, span.clone(), generator); + self.enqueue_generator("nix_eq", span, |co| { values.0.nix_eq(values.1, co, ptr_eq) }); return Ok(false); } GeneratorRequest::StringCoerce(val, kind) => { - self.reenqueue_generator(span.clone(), generator); - self.enqueue_generator(span, |co| val.coerce_to_string(co, kind)); + self.reenqueue_generator(name, span.clone(), generator); + self.enqueue_generator("coerce_to_string", span, |co| { + val.coerce_to_string(co, kind) + }); return Ok(false); } GeneratorRequest::Call(callable) => { - self.reenqueue_generator(span.clone(), generator); + self.reenqueue_generator(name, span.clone(), generator); self.tail_call_value(span, None, callable)?; return Ok(false); } @@ -347,7 +354,7 @@ impl<'o> VM<'o> { upvalues, light_span, } => { - self.reenqueue_generator(span, generator); + self.reenqueue_generator(name, span, generator); self.frames.push(Frame::CallFrame { span: light_span, @@ -423,14 +430,14 @@ impl<'o> VM<'o> { GeneratorRequest::TryForce(value) => { self.try_eval_frames.push(frame_id); - self.reenqueue_generator(span.clone(), generator); + self.reenqueue_generator(name, span.clone(), generator); debug_assert!( self.frames.len() == frame_id + 1, "generator should be reenqueued with the same frame ID" ); - self.enqueue_generator(span, |co| value.force(co)); + self.enqueue_generator("force", span, |co| value.force(co)); return Ok(false); } } diff --git a/tvix/eval/src/vm/macros.rs b/tvix/eval/src/vm/macros.rs index 4c027b0f6440..969b50807766 100644 --- a/tvix/eval/src/vm/macros.rs +++ b/tvix/eval/src/vm/macros.rs @@ -48,7 +48,7 @@ macro_rules! cmp_op { let gen_span = $frame.current_light_span(); $vm.push_call_frame($span, $frame); - $vm.enqueue_generator(gen_span, |co| compare(a, b, co)); + $vm.enqueue_generator("compare", gen_span, |co| compare(a, b, co)); return Ok(false); }}; diff --git a/tvix/eval/src/vm/mod.rs b/tvix/eval/src/vm/mod.rs index e104c3d2e7f9..78dc566ae729 100644 --- a/tvix/eval/src/vm/mod.rs +++ b/tvix/eval/src/vm/mod.rs @@ -125,6 +125,9 @@ enum Frame { /// be suspended while waiting for the VM to do something (e.g. /// thunk forcing), and resume at the same point. Generator { + /// human-readable description of the generator, + name: &'static str, + /// Span from which the generator was launched. span: LightSpan, @@ -296,11 +299,13 @@ impl<'o> VM<'o> { // Handle generator frames, which can request thunk forcing // during their execution. Frame::Generator { + name, span, state, generator, } => { - self.observer.observe_enter_generator(frame_id, &self.stack); + self.observer + .observe_enter_generator(frame_id, name, &self.stack); let initial_msg = if catchable_error_occurred { catchable_error_occurred = false; @@ -309,16 +314,24 @@ impl<'o> VM<'o> { None }; - match self.run_generator(span, frame_id, state, generator, initial_msg) { - Ok(true) => self.observer.observe_exit_generator(frame_id, &self.stack), - Ok(false) => self - .observer - .observe_suspend_generator(frame_id, &self.stack), + match self.run_generator(name, span, frame_id, state, generator, initial_msg) { + Ok(true) => { + self.observer + .observe_exit_generator(frame_id, name, &self.stack) + } + Ok(false) => { + self.observer + .observe_suspend_generator(frame_id, name, &self.stack) + } Err(err) => { if let Some(catching_frame_idx) = self.try_eval_frames.pop() { if err.kind.is_catchable() { - self.observer.observe_exit_generator(frame_id, &self.stack); + self.observer.observe_exit_generator( + frame_id, + name, + &self.stack, + ); catchable_error_occurred = true; // truncate the frame stack back to the @@ -395,7 +408,7 @@ impl<'o> VM<'o> { // OpAdd can add not just numbers, but also string-like // things, which requires more VM logic. This operation is // evaluated in a generator frame. - self.enqueue_generator(gen_span, |co| add_values(co, a, b)); + self.enqueue_generator("add_values", gen_span, |co| add_values(co, a, b)); return Ok(false); } @@ -450,7 +463,7 @@ impl<'o> VM<'o> { let a = self.stack_pop(); let gen_span = frame.current_light_span(); self.push_call_frame(span, frame); - self.enqueue_generator(gen_span, |co| { + self.enqueue_generator("nix_eq", gen_span, |co| { a.nix_eq(b, co, PointerEquality::ForbidAll) }); return Ok(false); @@ -548,7 +561,7 @@ impl<'o> VM<'o> { let gen_span = frame.current_light_span(); self.push_call_frame(span, frame); - self.enqueue_generator(gen_span, |co| { + self.enqueue_generator("coerce_to_string", gen_span, |co| { value.coerce_to_string(co, CoercionKind::Weak) }); @@ -665,7 +678,7 @@ impl<'o> VM<'o> { .map(|frame| frame.upvalues.with_stack_len()) .unwrap_or(0); - self.enqueue_generator(op_span, |co| { + self.enqueue_generator("resolve_with", op_span, |co| { resolve_with( co, ident.as_str().to_owned(), @@ -752,7 +765,7 @@ impl<'o> VM<'o> { let gen_span = frame.current_light_span(); self.push_call_frame(span, frame); - self.enqueue_generator(gen_span, |co| thunk.force(co)); + self.enqueue_generator("force", gen_span, |co| thunk.force(co)); return Ok(false); } } @@ -873,9 +886,10 @@ impl<'o> VM<'o> { BuiltinResult::Partial(partial) => self.stack.push(Value::Builtin(partial)), // Builtin is fully applied and the generator needs to be run by the VM. - BuiltinResult::Called(generator) => self.frames.push(Frame::Generator { + BuiltinResult::Called(name, generator) => self.frames.push(Frame::Generator { generator, span, + name, state: GeneratorState::Running, }), } @@ -922,7 +936,7 @@ impl<'o> VM<'o> { .map(|p| p.current_light_span()) .unwrap_or_else(|| self.reasonable_light_span()); - self.enqueue_generator(gen_span, |co| call_functor(co, val)); + self.enqueue_generator("__functor call", gen_span, |co| call_functor(co, val)); Ok(()) } v => Err(self.error(ErrorKind::NotCallable(v.type_of()))), @@ -1104,7 +1118,7 @@ pub fn run_lambda( // Synthesise a frame that will instruct the VM to deep-force the final // value before returning it. - vm.enqueue_generator(root_span.into(), final_deep_force); + vm.enqueue_generator("final_deep_force", root_span.into(), final_deep_force); vm.frames.push(Frame::CallFrame { span: root_span.into(), |